記法、引数、戻り値、変数(local, local -r)。
成果物
構文
MyFunc() { echo F; } MyFunc
| 意味 | コード例 |
|---|---|
| 定義 | MyFunc() { echo F; } |
| 実行 | MyFunc |
関数はfunctionを付与して定義するが省略できる。
function MyFunc() { echo F; }
引数
$n(nは数値)で位置引数を取得する。
Sum() { echo $(($1 + $2)); } echo $(Sum 10 20)
$0はスクリプトファイルパス。コマンドラインシェルのときはシェル実行ファイル名。
ParentDir="$(dirname $0)"
$@はすべての引数。""で囲むとスペースも含まれたひとつの文字列になる。""で囲まないとスペース区切りの単語になる。
Msg() { echo "$@"; } Msg "やあ" "hello world."
Msg() { for msg in $@; do echo $msg; done; } Msg "やあ" "hello world."
getoptsコマンドで引数解析できるが、複雑な解析はできない。
終了コード取得
MyFunc() { return 255; } MyFunc RET=$? echo $RET
関数は終了コードを設定できる。return(exit)の第一引数に0〜255の数値を渡すことで。0が正常、1〜255が異常。その他の値は不可。
もしreturnに文字列を指定すると実行時エラーになる。
MyFunc() { return 'A'; } MyFunc
bash: return: A: 数字の引数が必要です
stdout取得
MyFunc() { echo 'RESULT'; } RET="$(MyFunc)" echo $RET
関数から文字列を受け取りたいなら、コマンド置換によりstdoutを取得する。
local変数
MyFunc() { local RET="RESULT"; echo "$RET"; } RET="$(MyFunc)" echo $RET
関数内でのみ使用可。localを付与すれば関数内のみ参照できる変数を使える。グローバル変数を作らないので名前汚染を防げる。
set -uで未割当時にエラーとすると確認できる。
set -u MyFunc() { local RET="RESULT"; echo "$RET"; } echo $RET
bash: RET: 未割り当ての変数です
同名のグローバル変数とローカル変数が宣言されたときの話。関数内での参照は同名グローバル変数よりも同名ローカル変数が優先して参照される。
RET=GLOBAL MyFunc() { local RET="RESULT"; echo "$RET"; } RET="$(MyFunc)" echo $RET
RET=GLOBAL MyFunc() { echo "$RET"; } RET="$(MyFunc)" echo $RET
同名ローカル変数があったとき同名グローバル変数を参照する方法はない。と思う。
local -r
localかつreadonlyにする。
MyFunc() { local -r RET="RESULT"; RET="A"; echo "$RET"; } RET="$(MyFunc)" echo $RET
再代入エラーが出る。
bash: RET: 読み取り専用の変数です
readonly
グローバル変数のみ対象。
readonly RET="RESULT" RET="A"
再代入しようとすると以下のエラーになる。
bash: RET: 読み取り専用の変数です
localとの併用はできない。ふつうに再代入されてしまう。
MyFunc() { local readonly RET="RESULT"; local readonly RET="A"; echo "$RET"; } MyFunc() { local readonly RET="RESULT"; local RET="A"; echo "$RET"; } MyFunc() { local readonly RET="RESULT"; RET="A"; echo "$RET"; } RET="$(MyFunc)" echo $RET
local -rを使うべし。
1行データずつ関数を呼ぶ
while readで1行ずつ処理する。
Msg() { echo "MSG: $1"; } seq 1 3 | while read line; do { Msg ${line}; }; done;
ワンライナーにすると以下。
Msg() { echo "MSG: $1"; }; seq 1 3 | while read line; do { Msg ${line}; }; done;
もし1行データを累計したいなら以下。
Add() { echo "$(($1+1))"; } seq 1 3 | ( SUM=0; while read line; do { SUM=$((SUM+$(Add $line))); }; done; echo $SUM )
ワンライナーにすると以下。
Add() { echo "$(($1+1))"; }; seq 1 3 | ( SUM=0; while read line; do { SUM=$((SUM+$(Add $line))); }; done; echo $SUM );
xargsで関数を呼ぶ
whileの代わりにxargsを使うと以下。
Msg() { echo "MSG: $1"; } export -f Msg seq 1 3 | xargs -I@ bash -c 'Msg @'
ポイントはexport -f Msg。xargs bash -c '...'だと別プロセスとなり関数が未定義になる。そこでシェル変数から環境変数へ出力してプロセス間共有させる。export -f 関数名で。
ワンライナーにすると以下。
Msg() { echo "MSG: $1"; }; export -f Msg; seq 1 3 | xargs -I@ bash -c 'Msg @';
集計はわからん。
対象環境
- Raspbierry pi 3 Model B+
- Raspbian stretch 9.0 2018-11-13
- bash 4.4.12
$ uname -a Linux raspberrypi 4.14.98-v7+ #1200 SMP Tue Feb 12 20:27:48 GMT 2019 armv7l GNU/Linux