シェルスクリプトを使っていて、スクリプトが長く見づらくなってきたので、関数でまとめようとしました。
しかし、シェルスクリプトの関数の戻り値は、他のプログラム言語の関数の戻り値と違っていて、同じように使うには工夫が必要でした。
ただ、ちょっとトリッキーな使い方になってしまったので、自分用に使い方をメモしておこうと思います。
シェルスクリプトの関数の問題点
関数はreturn
で戻り値を返せるのですが、返せるのは整数値1つのみです。
戻り値というより、関数が成功したか否かを示す終了ステータス値として使用され、一般的に関数が成功した場合は「0」を返すようにします。
では、情報を関数の呼び出し元に渡すにはどうすればいいかと言うと、標準出力経由で渡します。
しかし、標準出力にはデバッグ情報を出力することもあるので、戻り値としての情報と、デバッグ情報を分けて出力する必要があります。
解決案(配列利用)
デバッグ情報は標準エラーに出力し、戻り値情報を標準出力に出力してみました。
また、複数の値を戻せるよう、配列を介して値を戻すようにしました。
#!/bin/bash function test_array { val1=$1 val2=$2 # stderr echo "debug out: val1->${val1}, val2->${val2}" 1>&2 # set return values ret_val=($val1 $val2) # stdout return values echo ${ret_val[@]} # exit status return 0 } res=($(test_array 123 "abc")) echo ${res[0]} echo ${res[1]}
debug out: val1->123, val2->abc 123 abc
解決案(JSON利用)
配列を使って値を戻すと、空白文字を含む文字列を戻した際に、空白文字で値が分割されてしまいます。
そこで、いっそのこと値をJSONに格納してしまって返すようにしてみました。
#!/bin/bash function test_json { val1=$1 val2=$2 # stderr echo "debug out : val1->${val1}, val2->${val2}" 1>&2 # set return values ret_val=$(cat <<_JSON_ [ $val1, "$val2" ] _JSON_ ) # stdout return values echo $ret_val # exit status return 0 } res=$(test_json 123 "abc def") echo $res | jq -r '.[0]' echo $res | jq -r '.[1]'
debug out : val1->123, val2->abc def 123 abc def
その他感想など
綺麗に書きたかったんですけど、結局泥臭い使い方になってしまいました。
何故関数がこんな仕様になっているかというと、Linuxのコマンド同様、データの出力は標準出力(ストリーム)で行うことにより、それぞれがパイプでつなげるようになっているんですね。
理にはかなっているんですが、関数の書き方に限らず、シェルスクリプトが今どきのプログラム言語のように書けると、もっと生産性が上がって便利なんですけどね。
そういったシェルスクリプトを今風に書きたいという要望はあるようで、JavaScriptでシェルスクリプトを書く「zx」なども登場しています。
期待して「zx」は使ってみましたが、JavaScriptからシェルスクリプトを呼び出せるライブラリといった感じで、データのやり取りを標準出力経由でやることには変わりなく、あまり利便性は感じませんでした。
個人的には、今風に書ける、シェルスクリプトの新しいバージョンが出てくれると嬉しいです。