UNIX/Linuxにおけるシェルではコマンドライン上で、または、関数ファイルやスクリプト内で関数を定義することができます。
コマンドライン上で作成された関数は、その場ですぐに、また関数ファイルやスクリプトファイル内で定義された関数は、カレントシェルにファイルを読み込むことで関数をUNIX/Linuxコマンドのように利用できるようになります。
bash shell function/シェル関数1では、シェル関数の定義方法やファイルを利用せずコマンドラインで直接関数を定義してすぐに使用する方法や引数を取るコマンドのような関数サンプルなどに触れました。
ここでは、シェル関数をファイル内に定義した場合に関数を登録、削除する方法や戻り値、スクリプトとシェル関数の関係などを見ていきます。
関数ファイルやスクリプトファイル内で定義したbashのシェル関数をコマンドとして利用する場合には、現在のシェルにソースを取り込む為のコマンド[ . ]1つのドット(ピリオド)から成るソースコマンド、または[ source ]コマンドを使います。
$ . foo
$ source bar
念のために追記すると、これらのコマンドで登録するのはソースとなるファイル(ソースファイル)ですが、シェル関数は、「実行権限を付与したスクリプトのようなファイル名ではなく関数名」で利用します。
$ function_name
$ function_name arg
*実行はファイル名じゃなくて関数名
例えばソースファイルに複数の関数がある場合にも、ソースファイルを読み込んでおけば、ソースファイル内の各「関数名」で関数を利用することができます。
$ more foo.func
function_1() {...
function_2() {...
...
function_n() {...
--continue--(xx%)
*[ . ]、[ source ]で取り込めば、いくつあってもそれぞれ関数名で実行できる
$ function_1
$ function_2
...
$ function_n
*もちろん関数名は番号付けなどしなくても任意に付けることができる
こうして登録された関数だけを集めたシェル関数ソースファイルには、実行権限パーミッションは必要ありません。
仮に付与してもコマンドラインから使用するコマンドとしてなら特に問題はありませんが、必要ないのでわざわざ実行権限を許可する理由もありませんし、むしろセキュリティ上、不用意にやたらとパーミッション設定すべきではありません。
*関数だけを集めた関数ファイルに実行権限パーミッションは不要
但し、スクリプトファイル内で関数を定義する場合には、注意が必要でスクリプト内関数が認識されないケースがあります。
*スクリプト内で定義した関数の場合、当然実行パーミッションは付加してよいが要注意
スクリプトの場合、当然実行権限がないと実行できませんから必然的に実行権限パーミッションが必要になりますが、スクリプトの性質上、そこに定義されている関数が認識されないケースがあるからです。
スクリプトは常にサブシェル上で実行されるが
既定では関数のスコープはカレントシェル内のみ
よってそのままスクリプト内関数を使用しようとするとエラーに・・・
これは、スクリプトが起動される際、起動したシェル上ではなく、常にサブシェルを起動してそこで実行されるようになっていることに起因してスクリプト上で関数を利用しようとすると期せずしてcommand not foundというエラーになります。
とは言え、実行時に常にサブシェルを起動してしまうスクリプトでもこうしたシェル間での関数自体のスコープの問題をクリアして利用できるように、bashには関数名を共有する方法があります。
1つは、scriptを前述のドットコマンド.やsourceコマンドでカレントシェルに読み込む方法、もう1つは、環境変数やshell変数のようにshell関数を共有可能なメモリ空間に登録・設定する方法で、後者の方法の場合、
$ export -f 関数名
とし、登録した関数名を確認する場合には、
$ declare -f 関数名
と(setでシェル変数、printenvやenvで環境変数を確認することもできますが、declareにfオプションと関数名を渡すと定義した関数だけを表示)します。
また、登録した関数名を削除する場合には、
$ unset -f 関数名
とします。
exportはオプションなしでbashでお馴染みの環境変数の登録に利用しますが、-nオプションは環境変数から指定された(関数名ではなく)環境変数を削除する為に、そして前述のようにexportの-fオプションは指定した名称の関数を登録する為にあります。
ちなみにunsetにはこの他-vオプションを付ける場合とオプションを指定しない場合がありますが、両者の違いは前者が環境変数の削除専用、後者は環境変数を探してあれば削除、ない場合に同名の関数がないかを探してあれば削除するという仕様になっています。
unset -v 変数名 ←変数削除専用
unset 名称 ←変数を探し、なければ関数を探し削除しようとする
但し、いくつか無効にできない、または読み取り専用の変数もあると[ help unset ]に書いてあります。
関数とは様子が変わり、逆に変数は、それらシェル間で共有されてしまうので時にはありがたい一方、各シェル間で同名の変数があった場合などにはバグの原因にもなりますが、bash組み込みコマンドにはlocalコマンドがあり、関数内で(Perlと異なり行末のセミコロンは不要ですが)Perl同様に変数の[local]宣言ができるようになっています。
local 変数名
local 変数名=値
Bourne Shell、bashでは同名のコマンドがあった場合の「既定の」実行の優先順位は、シェル関数、シェル組み込みコマンド、UNIX/Linuxシステムコマンドの順です。
Bourne Shellでは、この優先順位を変更するコマンドはなく、シェル組み込みコマンドを優先させることはできませんでしたし、システムコマンドもフルパス指定で実行する以外に術はありませんでしたが、bashでは、この優先順位を変更する為のシェル組み込みコマンドであるbuiltinコマンドとcommandコマンド、更にenableコマンドが追加されています。
前者2つは読んで字の如くですが、builtinコマンドはシェル関数ではなく組み込みコマンドを、commandコマンドはシェル関数でも組み込みコマンドでもなくシステムコマンドを指定する場合に、enableコマンドは既定ではシェル組み込みコマンド、システムコマンドの順ですが、-nオプションを指定するとシステムコマンドを優先します。
もちろんシェル関数内に制御構造を記述することもできますし、やはりたいていのスクリプトやプログラミング言語と同じように制御構造内部から関数をコールすることもできます。
bashシェル関数の戻り値は、Perl同様、returnで返し、returnを省略した場合には最後に評価された値が戻り値となりますが、Perlと異なり、returnの引数は数値のみです。