導入
シェルの変数は、何も指定しないとスクリプト全体で共有されます。関数の中で i を使ったら、外側のループの i を壊してしまった——これは実際に起きるバグです。local で防ぎましょう。
説明
local 変数名 と書くと、その変数は関数の中だけのものになります。関数を抜けると、元の値に戻ります。
#!/bin/bash
message="外側のメッセージ"
bad_function() {
message="関数が書き換えた"
}
good_function() {
local message="関数の中だけ"
echo "関数の中: $message"
}
echo "最初: $message"
bad_function
echo "bad のあと: $message"
message="外側のメッセージ"
good_function
echo "good のあと: $message"
実行すると、bad_function は外側の値を壊し、good_function は壊さないことが分かります。
flowchart TD
A["message=外側"] --> B{"関数の中で代入"}
B -->|"local なし"| C["外側の message が変わる(事故)"]
B -->|"local あり"| D["関数の中だけ変わる(安全)"]
D --> E["関数を抜けると外側は無傷"]
ルールはシンプルです。関数の中で新しく作る変数は、すべて local を付ける。付けて困ることはありません。特にループ変数(i・file・line)は事故が起きやすいので要注意です。
引数も同じ考え方で扱います。$1 をそのまま何度も使うより、冒頭で名前のある変数に受け直すと読みやすくなります。
process_file() {
local file="$1"
local mode="${2:-normal}" # 2つ目は省略可、既定は normal
echo "$file を $mode モードで処理"
}
やってみよう
スクリプトを実行して、bad のあと では値が書き換わり、good のあと では元のままであることを見比べましょう。次に good_function の local を消して実行すると、bad_function と同じ結果になります。local の有無だけで挙動が変わることを、自分の目で確かめてください。
演習
good_function を呼んだあとも外側の message が 外側のメッセージ のままであることを表示してください(good のあと: 外側のメッセージ が出ればOK)。
ヒント1を見る
変更は不要です。まずそのまま実行して結果を確かめてください。
ヒント2を見る
local を消すと結果が変わります。付け直すと元に戻ります。