Bashのシェル変数は name=value で設定し、等号の前後に空白を置きません。値を使うときは通常 "$name" と二重引用符で展開します。子プロセスへ渡すには export NAME、現在シェルから削除するには unset NAME です。NAME=value command はその一回のコマンド環境だけへ値を渡します。readonly NAME=value は現在シェルで再代入やunsetを禁止し、元へ戻すにはそのシェルを終了する必要があります。確認には declare -p、子環境の確認には秘密でないテスト値を使った env や bash -c を使います。 確認ポイント:shell変数とexport済み環境変数を分け、必要な子processだけへ値を渡します。
shell変数を引用付きで代入する
project_name="Quarterly report"
printf "value=<%s>\n" "$project_name"
declare -p project_name
期待出力は value=<Quarterly report> と、declare -- project_name="Quarterly report" に相当する宣言です。project_name = value と空白を入れると、Bashは project_name というコマンドを実行し、= と value を引数にしようとするため、通常はcommand not foundになります。
右辺では、単一引用符は内容を文字どおり保持し、二重引用符は変数展開やコマンド置換を許します。引用なしの値には空白をそのまま含められません。代入後の利用でも、printf "%s
" "$project_name" のように引用すれば一引数を保てます。引用なしの展開は単語分割とパス名展開を受けるため、意図した複数引数でない限り避けます。
変数名の境界とパラメーター展開
name=report
printf "%s\n" "${name}_2026.csv"
printf "default=%s\n" "${missing_value:-not-set}"
波括弧は変数名の境界を明確にします。期待出力は report_2026.csv と default=not-set です。ハイフンだけの既定値演算子は未設定時、コロン付きの演算子は未設定または空文字のときに既定語を使います。unsetと空文字を同じ扱いにするかで演算子を選びます。
value=""
[[ -v value ]] && printf "value is set\n"
[[ -z $value ]] && printf "value is empty\n"
unset value
[[ -v value ]] || printf "value is unset\n"
期待出力は順に value is set、value is empty、value is unset です。空文字代入は変数が存在する状態で、unsetは変数自体が未設定です。アプリケーションによって空文字を「明示的に無効」、未設定を「既定値使用」と解釈する場合があるため、置き換え可能とは限りません。
exportで子プロセスへ渡す
APP_MODE=staging
declare -p APP_MODE
bash -c 'printf "before export=<%s>\n" "$APP_MODE"'
export APP_MODE
bash -c 'printf "after export=<%s>\n" "$APP_MODE"'
最初の子Bashでは before export=<>、export後は after export=<staging> が期待値です。シェル変数は現在シェルにありますが、export属性がなければ新しい子プロセスの環境へ入りません。export APP_MODE=staging と代入と属性付与を一行にすることもできます。
子プロセスは親から環境のコピーを受け取ります。子で APP_MODE=changed と再代入しても、親シェルのAPP_MODEはstagingのままです。exportは双方向同期ではありません。新しく起動した子だけがその時点の値を受け取り、すでに動作中のプロセスの環境は更新されません。
envで公開範囲を確認する
env | grep -E '^APP_MODE='
bash -c "env | grep -E '^APP_MODE='"
declare -p APP_MODE
テスト値stagingなら、親から起動したenvと子Bashのenvに APP_MODE=staging が表示されます。declare -p APP_MODE はBash内の値とexport属性を確認できます。実環境で env 全体をログやチャットへ貼ると、トークンや接続情報が含まれる可能性があります。確認対象を秘密でない変数名一つへ絞り、値を公開してよいテスト文字列にします。
秘密値の有無だけを確認する場合は値を表示せず、[[ -v SECRET_NAME ]] や、子での存在判定だけを使います。export -p も全export変数を表示するため、共有ログへ保存しません。この記事の例はAPP_MODE、project_nameなど公開して問題ないダミー値だけを使います。
一つのコマンドだけに環境変数を渡す
APP_MODE=preview bash -c 'printf "child=<%s>\n" "$APP_MODE"'
printf "parent=<%s>\n" "$APP_MODE"
子の期待出力は child=<preview>、親は前に設定した parent=<staging> です。先頭の APP_MODE=preview はそのbashコマンドの環境へだけ適用され、現在シェルの値を上書きしません。アプリケーションの一回限りのモード、ロケール、設定先を試すのに使えます。
複数の一時環境を渡すなら NAME1=value1 NAME2=value2 command と並べます。外部入力をそのまま代入文へ連結せず、値を引用します。コマンドが子プロセスを起動すれば、その子にもexportされた一時環境が継承されることがあります。処理のログに値を出すアプリケーションもあるため、秘密情報を例のように平文で渡すことは避けます。
unsetで変数または関数を解除する
temporary_flag=enabled
declare -p temporary_flag
unset temporary_flag
declare -p temporary_flag
printf "declare exit=%s\n" "$?"
unset後の declare -p は「not found」相当を標準エラーへ出し、終了値1になるのが期待です。変数を対象にする意図を明確にするなら unset -v temporary_flag、関数なら unset -f function_name を使えます。名前を変数展開で組み立てて多数解除せず、対象名を固定して確認します。
APP_MODEを解除するなら unset APP_MODE の後、declare -p APP_MODE が失敗し、bash -c の子にも見えないことを確認します。元の値へ戻す必要がある一時変更では、最初からサブシェル ( APP_MODE=...; command ) またはコマンド前代入を使えば、親の値を保存・復元する処理を減らせます。
readonlyは現在シェルで解除できない
( readonly BUILD_ID=20260717
declare -p BUILD_ID
BUILD_ID=other
printf "assignment exit=%s\n" "$?"
)
printf "parent continues\n"
サブシェル内では declare -r BUILD_ID="20260717" 相当が表示され、再代入はreadonly variableエラーになります。readonly変数は通常unsetもできません。例を括弧内へ限定したので、サブシェル終了と同時にBUILD_IDは親へ残りません。現在の対話シェルで誤ってreadonlyにした場合、値を解除して元へ戻すのではなく、そのシェルを終了して新しいシェルを開始します。
readonlyはファイルや外部プロセスから値を保護するセキュリティ境界ではなく、そのBashプロセス内の再代入を防ぐ属性です。子プロセスへ渡すには別途exportが必要で、子が受け取った環境値を自身で変えることはできます。変更不能な定数としてシェルスクリプト内で使う場合も、設定順とエラー時の終了をテストします。
配列は要素ごとに引用して扱う
items=("red item" "blue" "green")
declare -p items
printf "item=<%s>\n" "${items[@]}"
printf "count=%s\n" "${#items[@]}"
期待出力は空白を含む red item も一要素として、3行のitemと count=3 です。配列の@展開を二重引用符で囲むと、各要素を別引数のまま展開します。引用なしやアスタリスク形式の展開では要素境界が変わるため、コマンドへ複数引数として渡す用途では配列全体を引用した@展開にします。
Bashのインデックス配列は items[0] のように添字で参照できます。要素に空白やワイルドカード文字が含まれても、引用した@展開なら一要素として保たれます。配列をexportして一般的な子プロセス環境へ同じ構造のまま渡すことはできません。必要なら安全なシリアライズ形式を決め、子側で明示的に解析します。
nounsetと未定義配列の注意
( set -u
empty_items=()
printf "defined count=%s\n" "${#empty_items[@]}"
printf "safe item=<%s>\n" "${missing_items[@]-}"
)
set -u または set -o nounset は、未設定変数の展開をエラーにします。空配列と未定義配列、Bashの版、添字展開によって挙動の差が問題になりやすいため、配列は empty_items=() と明示的に初期化してから使います。任意配列を扱うコードでは、存在確認 declare -p name や既定値展開を設計します。
nounsetを対話シェル全体で試すと、既存のプロンプトや補助スクリプトが未設定変数を参照してシェルが終了することがあります。例のようにサブシェルへ限定すれば、親シェルのオプションを変えません。set -u があるから引用、型、空配列のテストが不要になるわけではありません。
子シェルで継承を検証する一式
VERIFY_COLOR=amber
printf 'shell: '; declare -p VERIFY_COLOR
printf 'environment before export: '; env | grep -E '^VERIFY_COLOR=' || printf 'absent\n'
export VERIFY_COLOR
bash -c 'printf "child: VERIFY_COLOR=<%s>\n" "$VERIFY_COLOR"'
unset VERIFY_COLOR
bash -c "env | grep -E '^VERIFY_COLOR=' || printf 'child: absent\n'"
期待結果は、最初のdeclareではamber、export前のenvではabsent、export後の子ではamber、unset後に新しく起動した子ではabsentです。これで「シェルに設定」「環境へexport」「子が継承」「親でunset」の各段階を、秘密を含まない一つの変数だけで確認できます。
export済み変数を元のscopeへ戻す
通常の代入、export、unsetは現在シェルの状態を変えます。検証を括弧のサブシェル内へ置くか、NAME=value command を使えば終了時に親へ残りません。現在シェルで設定したテスト変数は unset -v 名前 で解除します。readonlyは同じシェルで解除できないため、検証は必ずサブシェルへ限定します。シェル起動ファイルへ値を書き込む永続設定はこの記事の実演に含めません。
変数scopeを閉じる確認
- 代入の等号前後に空白を置かず、展開を二重引用した。
- 未設定と空文字、シェル変数とexport済み環境変数を区別した。
NAME=value commandで一回だけの環境を検証した。- readonlyとnounsetはサブシェルへ限定し、親シェルを壊していない。
- 配列を初期化し、
declare -pと秘密でない子シェルテストで継承を確認した。

コメント