それマグで!

知識はカップより、マグでゆっくり頂きます。 takuya_1stのブログ

習慣に早くから配慮した者は、 おそらく人生の実りも大きい。

bashで関数の定義と実行と削除

bashの関数

bash は 組込コマンド(組込み関数) は $PATH の通っている コマンドの実行 の他に、自分で定義した関数 function を使うことが出来ます。

シェルスクリプトを記述する プログラミング言語としておなじみの機能です。

bashの関数の定義方法

bash の関数定義の方法は、よく見る関数定義と同じですが、記述方法が3つ + 1つあります。

function MY_FUNC () {
  echo hello world
}
function MY_FUNC {
  echo hello world
}
MY_FUNC () {
  echo hello world
}
MY_FUNC ()  for (( idx=0; idx<10; idx++ )); do echo $idx ; done 

このどれもが、関数の定義として作用します。

特に最後の一つは馴染みがないかもしれません。でもちゃんと動く関数定義です。

最後の一つは、省略記法になっていて、for / if / whileなどの制御構造と、[[ expression ]]であれば記述することが可能です

{} は複数コマンドを記述する

もともと bash には { } をつかい、複数行のコマンドを実行するという機能があります。

複数行をまとめて実行する例
takuya@Desktop$ { echo a; echo b; }
a
b

これらは、crontab で複数コマンドを実行するときなどに使うと便利な記述です。

関数定義の後ろにくっついた { } も同じように解釈すると理解が進むと思います。

関数を定義して使う

bash は上から下に順番に実行される、逐次処理するインタプリタなので、関数は定義してから使います。

幾つかのプログラミング言語では、関数の定義と実行は順序無くかけるかもしれません。しかし bash では前から処理されるため、使う箇所より定義が先である必要があります*1

また、呼び出すときは、関数の名前だけを指定して、( )は特につけません。コマンドと同等に使うことが出来ます。

php での関数の後置記述の例
<?php
sample();
function sample(){
 echo 'hello \n';
}
bash での関数定義と呼び出し
function sample(){
 echo "Hello world"
}
sample

関数を削除する

定義済み関数を削除するには unset を使います。

これは、関数も変数として格納されているためです。

定義済み関数を削除する例
function my_sample(){
 echo "Hello world"
}
my_sample
unset my_sample
削除した関数を呼び出した場合
$ my_sample
-bash: my_sample: コマンドが見つかりません

削除した関数を呼び出したら、当然ですが、未定義になっています。

関数は変数から呼び出せる

関数の名前を格納した変数を実行することで、関数を呼び出すことが出来ます。これにより、関数呼び出しを抽象化することが可能になります。

変数に格納した関数名を呼び出す
function my_sample(){
 echo "Hello world."
}
function_name=my_sample
$function_name #=> Hello world.

これは、bash が逐次処理され、変数名が実行前に展開されることに由来すると考えられます。

つまり $function_name は実行前に my_sample に展開され、関数として実行されるのです。

関数の引数

関数は引数を取ることが出来ます。

これらは、通常のシェルスクリプトに与えた引数と同等に扱うことが出来ます。

function arg_sample(){
 echo $@
}
arg_sample a b c d 

関数の引数は、シェルスクリプトに与えた変数と同じなので、getopts で解釈することもの出来ます。このあたりは、bashシェルスクリプトに与える引数getopts について書くときに詳しく書きたいと思います。

関数はコマンドより優先される

関数はコマンドより優先されます。つまりどういうことかというと、 $PATH で指定したコマンドより、関数が優先されます。

つまり、コマンドと同名の関数があれば、関数が実行されるのです

takuya@Desktop$ function ls(){ echo dumy;  }
takuya@Desktop$ ls
dumy

これを使って、一時的や便利のために、コマンドを上書きする関数が作れます。もちろんイタズラにつかうと、とんでもないことになるしセキュリティ懸念がありますが。

alias では実現できない、関数の上書きを function を使って実現することが出来ます。私は find コマンドを上書きして使っています。

alias と function が衝突した場合

alias と fucntion に同名の定義がある場合。alias が優先されます。

alias は 関数への alias も出来てしまうので、関数より優先されるのが自然な動作です。

同名は alias が優先
alias find='echo find from alias'
function find() {  echo find from function ;}
type find
#=>find は `echo find from alias' のエイリアスです
関数への alias の定義の例
function my_func { echo from function $@ ;  }
alias my_func='my_func with alias '

my_func #=> from function with alias

コマンドを上書きするときは、unaliasunset を使って上手に名前の衝突を回避する必要があるかもしれません。

関数の戻り値

function my_func() {
   return 0
}

関数の戻り値は、正常終了で0、それ以外で1以上を返す。

処理を終えるのなら exit 0 で正常終了 exit 1 で異常終了を返してもいいんだけど、自分で書いたぱぱっとスクリプトで return /exit を細かく維持しても・・・

TODO 戻り値の、終了コードについてもう少し詳しく書く。制約とか、文字列を渡す場合のechoとか。

おまけ

javascript の関数定義で見かける、無名関数や即時関数みたいなことも出来ます。

変数の名前空間を汚さないので、使い方によっては便利に使えるかもしれません。

即時関数の例
takuya@Desktop$ (function a() { echo a; }; a )
a
takuya@Desktop$ a
-bash: a: コマンドが見つかりません
takuya@Desktop$

まとめ

  • 関数の定義方法
    • name ()
    • name () {}
    • fucntion name () {}

*1: php が事前にコンパイルが走って、関数一覧を先に処理してくれる. php特殊なんだと思いますが、、、