Linuxでのシェルオプションのセット・アンセット方法

Bash の動作オプションは、set が扱うシェル属性と、shopt が扱う Bash 固有機能に分かれます。名前が似ていても切替コマンドは共通ではありません。以下は Bash で実行する手順です。/bin/sh が dash などを指す環境では shopt、pipefail、BASHOPTS は使えません。最初に bash --version と printf '%s\n' "$BASH_VERSION" で実行シェルを確かめます。 確認ポイント:set optionとshopt optionは別体系なので、変更前の出力を保存して同じbuiltinで復元します。

目次

set -oとshopt -pで現在値を保存する

set -o は全オプションを名前と on/off で表示し、set -o pipefail は有効化、set +o pipefail は無効化します。短縮形では set -e/set +e、-u/+u、-x/+x がそれぞれ errexit、nounset、xtrace に対応します。プラスが「解除」なのが重要です。現在値だけを機械的に調べるなら、set -o pipefail の表示ではなく [[ -o pipefail ]] の終了状態を使えます。

set -o | grep -E '^(errexit|nounset|pipefail|xtrace)[[:space:]]'
[[ -o pipefail ]]; printf 'pipefail status=%d\n' "$?"
set +o

初期状態なら表示例は errexit off、nounset off、pipefail off、xtrace off です。ただし対話シェルや起動ファイルで既に変更されていれば値は異なります。最後の set +o は、現在状態を再現できる set +o errexit などのコマンド列を出すため、変更前スナップショットとして保存できます。

shopt -s nullglob は有効化、shopt -u nullglob は無効化、shopt -p nullglob は再実行可能な状態を表示します。状態確認の終了値は有効なら 0、無効なら非 0 です。shopt -p は全項目、shopt -s または -u は名前を省略すると、それぞれ有効・無効の項目を一覧にします。

shopt -p nullglob
shopt -s nullglob
shopt -q nullglob; printf 'enabled=%d\n' "$?"
shopt -u nullglob

上の実行結果は、初期状態が無効なら最初が shopt -u nullglob(終了値 1)、有効化後の確認が enabled=0 です。初期状態は環境依存なので、表示文字列ではなく shopt -q の終了値を自動判定に使います。

使い捨てスクリプトで -e、-u、-x、pipefail を検証する

既存スクリプトへいきなり追加せず、空の一時ディレクトリで挙動を分離します。次のヒアドキュメントは options-demo.sh を作り、各ケースを別の Bash 子プロセスで実行します。最後の trap はこの検証で作ったディレクトリだけを終了時に消す復元処理です。

work=$(mktemp -d) || exit 1
trap 'rm -r -- "$work"' EXIT
cat >"$work/options-demo.sh" <<'BASH'
#!/usr/bin/env bash
case $1 in
  e) set -e; false; echo 'unreachable' ;;
  u) set -u; printf '%s\n' "$NOT_DEFINED" ;;
  x) set -x; value=7; printf 'value=%s\n' "$value"; set +x ;;
  p)
    set -o pipefail
    false | true
    rc=$?
    printf 'pipeline=%d\n' "$rc"
    exit "$rc"
    ;;
  off) set -eu; set +e +u; false; printf 'unset=%s status=%d\n' "${NOT_DEFINED-OK}" "$?" ;;
esac
BASH
chmod 700 "$work/options-demo.sh"
out="$work/options-out"
err="$work/options-err"
for mode in e u x p off; do
  printf '%s: ' "$mode"
  bash "$work/options-demo.sh" "$mode" >"$out" 2>"$err"
  rc=$?
  printf 'rc=%d stdout=<%s>\n' "$rc" "$(cat "$out")"
  if [[ $mode == x ]]; then sed 's/^/stderr: /' "$err"; fi
done

期待値は e: rc=1 stdout=<>、u: rc=1 stdout=<>、p: rc=1 stdout=<pipeline=1>、off: rc=0 stdout=<unset=OK status=1> です。x の標準出力は value=7、標準エラーには + value=7 と + printf ... のトレースが出ます。Bash の版や PS4 により引用表示は多少変わるため、検証では終了値と出力値を主に見ます。-x は展開後の値を標準エラーへ出すので、パスワードやトークンを扱う区間では set +x に戻します。

errexit の例外と、エラー処理の代わりにならない理由

set -e は「どの非 0 でも必ず終了」ではありません。while/until の条件リスト、if の判定、最後以外の &&/|| 要素、! で反転したコマンド、pipefail が無効なパイプラインの最後以外などでは終了しない条件があります。また、呼出し側が終了判定を無視する文脈に関数を置くと、その関数本体での効き方も直感とずれることがあります。

bash -c 'set -e; if false; then echo no; fi; echo after-if'
bash -c 'set -e; false || echo recovered; echo after-or'
bash -c 'set -e; false | true; echo after-pipe'

正確な出力は順に after-if、recovered と after-or、after-pipe です。最後の例へ set -o pipefail を加えると終了値 1 で after-pipe は出ません。したがって errexit は早期停止の方針には使えても、失敗理由の分類、再試行、利用者向けメッセージ、後始末を実装しません。期待される非 0 は if command; then ...; else rc=$?; ...; fi で明示的に扱い、必要な終了値を返すのが堅実です。

コマンド置換 value=$(...) では、Bash は既定で置換用サブシェルの -e を解除します。shopt -s inherit_errexit を有効にするか POSIX モードにすると継承されますが、既存スクリプトの停止点が変わるため、変更前に shopt -p inherit_errexit を保存し、テスト後はその出力を実行して元へ戻します。

オプションの継承範囲と復元

丸括弧のサブシェル ( ... ) は親のオプション状態を複製して始まり、内部の変更は親へ戻りません。一方、別プログラムとして起動した bash -c は同じものではありません。SHELLOPTS は有効な set -o 系、BASHOPTS は有効な shopt 系をコロン区切りで保持する読み取り専用変数です。これらが環境に存在して Bash が起動すると、列挙されたオプションを起動前に有効化します。暗黙の普遍的継承と考えず、必要なら明示して検証します。

set -o pipefail
shopt -s nullglob
printf 'parent pipe=%s null=%s\n' "$([[ -o pipefail ]] && echo on)" "$(shopt -q nullglob && echo on)"
( set +o pipefail; shopt -u nullglob; printf 'sub pipe=%s null=%s\n' "$([[ -o pipefail ]] && echo on || echo off)" "$(shopt -q nullglob && echo on || echo off)" )
printf 'parent-again pipe=%s null=%s\n' "$([[ -o pipefail ]] && echo on)" "$(shopt -q nullglob && echo on)"
export SHELLOPTS BASHOPTS
bash -c 'printf "child pipe=%s null=%s\n" "$([[ -o pipefail ]] && echo on)" "$(shopt -q nullglob && echo on)"'

出力は parent pipe=on null=on、sub pipe=off null=off、parent-again pipe=on null=on、child pipe=on null=on です。作業シェル自体を変更した場合の復元は、事前に set +o >set-state.sh と shopt -p >shopt-state.sh を同じシェルで保存し、確認後に source set-state.sh; source shopt-state.sh とします。信頼できない状態ファイルは source しないでください。スクリプトでは、必要なオプションを冒頭で明示し、終了時の親シェルへ影響しない性質を利用する方が再現しやすくなります。

set・shoptの復元確認

  • bash -n script.sh が 0 で、構文エラーがない。
  • 正常系・想定失敗・空入力を別々に実行し、終了値と標準エラーを記録した。
  • set -o と shopt -p で実際の有効状態を確認した。
  • xtrace に秘密値が含まれず、必要区間の後で set +x に戻っている。
  • set -e に任せず、回復が必要な失敗を明示的な条件分岐で処理している。

公式情報・参考資料

この記事を書いた人

実務の現場で詰まりがちなポイントを地図にするITブログ「IT trip」を運営。Windows/Office(Teams・Excel)からSQL、サーバ運用、ガジェットまで、再現性のある手順と“なぜそうなるか”を丁寧に解説します。読んだらすぐ試せること、そして迷った人の次の一歩が見えることを大切にしています。

コメント

コメントする

目次