プログラミングをやったことがある人にはお馴染みだろうが、シェルスクリプトでも関数を作ることができる。
関数とは処理をまとめて、何度も使えるようにしたものだ。
シェルスクリプトは、もともとあまり複雑なプログラムを組むのに適してはいないが、関数を少し使って効率良く作成するくらいであれば構わないだろう。
このページでは関数の使い方についてご紹介する。参考にしてほしい。
目次
簡単に関数とは?
簡潔にいえば「処理をまとめたものが関数」である。中学校のとき習った次のような関数と同じだ。
y = 2x
xに何かを代入したら、yの値も決まる。
基本的に「引数を受け取り、それを処理し結果を返す」というのが役割である。とはいえ、引数を受け取らなくても動く関数もあるので、同じ動作を何度も書かないでしてくれる便利な方法だと思っておけばいい。
関数を使用すると同じ処理をするコードを繰り返し記述する必要がなくなるのである。
書式について
次のような書式になる。
function 関数名 () {
処理...
}
もしくは「function」を省略して
関数名 () {
処理...
}
とすることが出来る。関数を使うには、
関数名 引数
で使うことができる。
実際にやってみよう
書式をみせられてもピンとこないだろうと思うので実際に書いてみよう。
vi func-ex1.sh
1 2 3 4 5 6 7 8 |
#!/usr/bin/bash # 「hello_world」と表示する関数 function hello_world () { echo hello_world } #関数の呼び出し hello_world |
実行結果
$ ./func-ex1.sh
1 |
hello_world |
関数の使い方の詳しいところ
関数の引数とパラメータ
関数の場合引数を受け取る場合も多々ある。関数の機能によっては引数をとらないこともあるが、以下の様にすると引数のパラメータを確認できる。基本的にスクリプト実行時のパラメータと使い方は同じである。
vi func-ex2.sh
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
#!/usr/bin/bash testfunc() { #実行スクリプト名となる。 echo "\$0=$0" #引数の数 echo "\$#=$#" #渡たされた引数を全て展開したもの echo "\$@=$@" echo "\$*=$*" #各引数に応じた番号$N echo "\$1=$1" echo "\$2=$2" echo "\$3=$3" } testfunc aaa bbb ccc |
実行結果
$ ./func-ex2.sh
1 2 3 4 5 6 7 |
$0=./func-ex2.sh $#=3 $@=aaa bbb ccc $*=aaa bbb ccc $1=aaa $2=bbb $3=ccc |
戻り値について
bashシェルスクリプトには「戻り値」というものは基本的に存在しない。
解決策として関数やコマンドの「実行結果を直接変数に代入する」という手段をとることになる。「return」コマンドは存在するが、あくまで終了ステータスをかえしているだけで、関数の戻り値としての機能ではないので注意しよう。
vi func-ex3.sh
1 2 3 4 5 6 7 8 |
#!/usr/bin/bash testfunc() { echo "実行結果を戻り値とする。" echo "hello_world" } #実行結果を代入 return_var=`testfunc` echo "$return_var" |
実行結果
$ ./func-ex3.sh
1 2 |
実行結果を戻り値とする。 hello_world |
変数の範囲
基本的にbashは指定しない限り、変数はグローバルなものとして扱われる。「グローバル変数」とはどこからでも参照出来る変数のことだ。下記の例をみていただきたい。
vi func-ex4.sh
1 2 3 4 5 6 7 8 9 10 |
#!/bin/bash function testfunc1() { testvar="hoge" } function testfunc2(){ echo $testvar } testfunc1 testfunc2 echo $testvar |
実行結果
$ ./func-ex4.sh
1 2 |
hoge hoge |
このようにtestfunc1の内部で宣言した変数が特殊なことなしにtestfunc2で参照できる、その他の場所からでもechoで参照できる。
現在一般的なコンピュータ言語とくらべるとその挙動はルーズなものかもしれないが、bashはインタプリタでありシェルである。機能上、環境変数などをグローバルに参照しなければならない。こういった作りのほうが都合がよいことが多いのだろう。
では「関数内だけのローカル変数は作成できないのか?」というと、ちゃんと仕組みは用意されている。「local」というビルドインコマンドを使用するとローカル変数を作成できる。
vi func-ex5.sh
1 2 3 4 5 6 7 8 9 10 |
#!/usr/bin/bash function testfunc1() { #localを追加する。 local testvar="hoge" echo "関数内部testvarの値は$testvar" } testfunc1 #変数は参照できなくなる。 echo $testvar echo 外部からは参照できないため空白となる。 |
実行結果
$ ./func-ex5.sh
1 2 3 |
関数内部testvarの値はhoge 外部からは参照できないため空白となる。 |
このように「local」を使用すると関数内部だけで使用する変数を作成できる。
小さなスクリプトならばグローバル変数だけでも問題にはなりにくいが、規模が大きくなると「どこで変数の値が変化するのかわからなくなる」という状態が発生する。
そのため大きめのスクリプトを記述する時や、意図的に変数ローカルにし外部からの変更を受付ないようにする、といった状況で使用するといいだろう。
複数のファイルに分割して記述する
ファイルを分割して管理するほど、大規模になるとシェルスクリプトではなく他の本格的なコンピュータ言語による実装を考えた方がよいと思われるが、出来ないという訳ではない。例えば以下のようになる。
vi myfunc_rsq.func
1 2 3 4 |
function myfunc_return_square(){ RETURN_VAR=`expr $1 \* $1` echo $RETURN_VAR } |
vi func-ex6.sh
1 2 3 4 5 |
#!/usr/bin/bash #myfunc.func取り込み「.」を忘れないようにしよう。 . myfunc.func #関数の呼び出し myfunc_return_square 6 |
実行結果
$ ./func-ex6.sh
1 |
36 |
ここで重要なのは「.」である。
この「ドットコマンド」は他のスクリプトファイルを現在のカレントシェルに読み込み、変数や関数を使用出来るようにするコマンドだ。
これを活用することで、個々の断片的なスクリプトを取りこむことが出来るようになる。
まとめ
シェルスクリプトの関数についてまとめてきたが、いかがだっただろうか?
冒頭でもお伝えした通り、シェルスクリプトで大規模なプログラムを組むのはオススメしない。関数を少し使うくらいで止めておいたほうがいいだろう。
とはいえ、関数であれば使う場面は多いので、この機会に使い方は覚えてしまおう。
「複数のファイルに分割して記述する」で紹介されているfunc-ex6.shのリストですが、これは正しいのでしょうか。
実際に打ち込んで走らせてみると、エラーが出てしまいます。
3行目の. myfunc.funcを. myfunc_rsq.funcと、読み込むファイル名を略すことなく打ち込むと、そこで正常に動きます。