Bash function内のlocal variableで最初に求められるのはcommand暗記ではなく、何を正しい結果とするかの定義です。結論は「Bash functionではlocalで一時variableを閉じ込め、globalを書き換える副作用を減らします。ただしdynamic scope、array、nameref、subshellの挙動を明示します。」。BashのlocalはPOSIX shの必須機能ではなく、dynamic scopeで呼び出し先functionから見える場合があることを確認し、表示、加工、変更を混同しない順序で進めます。 確認ポイント:localはBash固有かつdynamic scopeであり、array出力はlocal配列そのものを展開します。
Bash interpreterとglobal変数を先に確認する
現在値を変更前snapshotとして保存します。存在確認だけでなく、所有者、権限、版、依存関係を必要な範囲で読み、backupが必要な対象を洗い出します。
- scriptのinterpreterがBashか確認する
- globalと同名のvariableを棚卸しする
- functionの入力とreturn方法を決める
- subshellとcommand substitutionの境界を確認する
権限errorをsecurity設定の弱体化で回避しません。担当者と必要なroleを確認してから再試行します。
scalar・command結果・arrayをlocal化する
基本のlocal variable
format_name() {
local first=$1 last=$2
printf '%s %s\n' "$first" "$last"
}
assignment右辺をquoteし、function外の同名variableを保護します。
local宣言とcommand結果を分離
load_value() {
local value
value=$(some_command) || return
printf '%s\n' "$value"
}
local value=$(…)ではcommand substitutionのstatusが隠れる実装差を避けます。
local arrayを使う
collect() {
local -a items=("$@")
printf '%s\n' "${items[@]}"
}
arrayに保存する場合もquoted引数で要素境界を保ちます。
readonly localを作る
show_root() {
[ "$#" -ge 1 ] || return 2
local -r root=$1
printf '%s\n' "$root"
}
function内で再代入しない入力を固定します。
呼び出し後のglobalを確認
value=global
f() { local value=local; printf 'inside=%s\n' "$value"; }
f
printf 'outside=%s\n' "$value"
sampleでscopeを目視し、globalが保持されることを確認します。
dynamic scopeを呼出し先まで確認する
Bashのlocalはfunction内でvariableのvisible instanceを作りますが、lexical scopeではなくdynamic scopeです。呼び出された別functionが同名localを参照する可能性があります。出力はstdout、statusはreturn codeというinterfaceに分けるとtestしやすくなります。
local代入で終了statusを隠さない
- POSIX shでもlocalが必ず使えると思う
- local assignmentでcommand failureを隠す
- dynamic scopeをlexical scopeと誤解する
- 配列をunquoted展開して要素を壊す
- stdoutのdataと診断messageを混ぜる
想定外を抑え込むためsecurity機能やcertificate検証を無効化しません。
引数不足とglobal上書きで停止する
secretをlocalにしてもmemory、trace、error logへの露出は防げません。set -x中は値を扱うfunctionの前後でtrace方針を見直します。global環境variableの上書きやexportをfunction内で行う場合は元値を保存し、trapを含む復元経路をtestします。
変更前後のhash、時刻、対象IDを一対一で結びます。戻す対象をwildcardで再探索しません。
function前後の値とstatusを記録する
globalとlocalの同名、nested function、command substitution、array空要素、failure statusをtestします。shellcheck等の補助結果だけに依存せず、対象Bash versionでstdout、stderr、statusを記録します。
成功だけでなく、0件とerror時に後続が止まることも確認します。
Bash固有機能とscope境界を引き継ぐ
対象範囲を確定する段階では「functionの入力とreturn方法を決める」が判断材料になります。また「subshellとcommand substitutionの境界を確認する」を満たさない場合は、技術的に実行できても運用上の準備不足です。
実例のうち「基本のlocal variable」はbaselineを得る用途、「local宣言とcommand結果を分離」は対象をさらに具体化する用途として使い分けます。両方の出力を同じ形式へ無理に整形せず、元の型と件数を保持したまま比較します。期待値は画面の見た目ではなく、対象ID、path、時刻など再照合できる列で定義します。
ローカル変数の動作は、同名のグローバル変数、入れ子の関数、配列、未設定値を含む小さな関数で確認します。関数の呼び出し前後でグローバル値が変わらず、ローカル値が戻り後に参照できないことを確かめてください。local の細かな挙動はシェルごとに異なるため、Bash向け手順を sh で実行せず、先頭行と実行シェルを一致させます。
関数の前後でグローバル値を表示すると、local が同名変数を呼び出し中だけ隠すことを確認できます。ただし関数が global=value のように明示代入すれば外側は変わります。またcommand substitutionやpipelineの実行位置ではsubshellになる場合があり、そこでの代入が親シェルへ戻らない点を、ローカル変数の効果と混同しないでください。
再試行の前に、POSIX shでも local が必ず使えると思っていないか、local value=$(command) がcommandの失敗値を隠していないか確認します。Bashのlocal変数は動的scopeを持つため、呼び出した別関数から見える場合があります。小さな入れ子関数で実際のscopeを確認します。
配列は "${array[@]}" のように引用して要素境界を保ち、診断messageは標準エラーへ分けます。ローカル変数にtokenなどを入れても、traceやexportで外へ出れば保護にはなりません。関数の確認でグローバル変数を書き換えず、変更例では呼び出し前の値を保存して戻せるようにします。
再現記録にはshellとversion、shebang、対象関数、呼び出し前・関数内・戻り後の値と終了値を含めます。配列なら要素数も照合し、command substitutionを使う場合は内側の失敗値を別に保存します。関数内だけの代入なら永続rollbackは不要です。
完了条件は、同名グローバルが関数終了後も元の値で、local変数が外へ漏れず、複数回呼び出して同じ結果になることです。入れ子関数と配列のfixtureも通し、失敗commandが成功として扱われていないことを確認します。
Bash function内のlocal variableの開始記録には「scriptのinterpreterがBashか確認する」を最初に置きます。続けて「globalと同名のvariableを棚卸しする」を確認すると、対象違いと環境違いを作業前に分けられます。

コメント