set オプション
| 対応: | set -e / set -u | POSIX(sh互換) |
|---|---|---|
| set -o pipefail | Bash(bash拡張) |
『シェルスクリプト』では、set コマンドでシェル自体の動作オプションを切り替えられます。特に set -e(エラー時即終了)・set -u(未定義変数をエラーにする)・set -o pipefail(パイプ内のエラーを検知する)・set -x(デバッグ出力)の4つは実用スクリプトで頻繁に使われます。これらを組み合わせた set -euo pipefail は「安全なスクリプトの先頭定番イディオム」として広く知られています。
主要オプション一覧
| オプション | 長いオプション名 | 説明 |
|---|---|---|
set -e | set -o errexit | コマンドが 0 以外の終了ステータスを返したとき、スクリプトを即座に終了します。意図しない失敗を見逃さないために使います。 |
set -u | set -o nounset | 未定義の変数を参照したときにエラーを発生させます。タイポによる変数名のミスを早期に検出できます。 |
set -o pipefail | — | パイプライン中のいずれかのコマンドが失敗した場合、パイプライン全体の終了ステータスをその失敗ステータスにします。-e だけでは検知できないパイプ内エラーを捕捉できます。 |
set -x | set -o xtrace | 実行する各コマンドを + プレフィックス付きで標準エラー出力に表示します。デバッグ時に何が実行されているかを追跡するために使います。 |
set +e | set +o errexit | -e を一時的に無効化します。失敗が想定されるコマンドを実行する区間で使います。 |
set +x | set +o xtrace | -x を無効化してデバッグ出力を止めます。 |
set -e(errexit)の挙動
set -e を有効にすると、コマンドが失敗した時点でスクリプトが終了します。ただし、if 文や while の条件部分・! で否定したコマンド・|| / && の右辺はこの対象から外れます。失敗が許容されるコマンドは command || true と書くと -e の影響を回避できます。
| 書き方 | 説明 |
|---|---|
command || true | コマンドが失敗しても終了ステータスを 0 にします。-e の影響を受けずに続行します。 |
if command; then ...; fi | if の条件式は -e の対象外です。失敗しても終了しません。 |
result=$(command) || true | コマンド置換も -e の対象です。失敗を許容するときは || true を付けます。 |
# ----------------------------------------------- # set -e の基本的な挙動 # ----------------------------------------------- set -e echo "処理を開始します" # 失敗するコマンド(存在しないファイルを削除しようとします) rm /tmp/no_such_file # ← ここで失敗してスクリプトが終了します echo "ここは実行されません" # ← 到達しません
$ bash -e demo.sh 処理を開始します rm: cannot remove '/tmp/no_such_file': No such file or directory
set -u(nounset)の挙動
set -u を有効にすると、値が設定されていない変数を参照した時点でエラーになりスクリプトが終了します。変数名のタイポを検出する強力なセーフガードになります。デフォルト値を持たせたい場合は ${VAR:-デフォルト値} のパラメータ展開を使います。
| 書き方 | 説明 |
|---|---|
${VAR:-default} | 変数が未定義または空のとき default を使います。-u 環境でもエラーになりません。 |
${VAR:?メッセージ} | 変数が未定義または空のとき メッセージ を表示してスクリプトを終了します。 |
${VAR-default} | 変数が未定義のときだけ default を使います(空文字は対象外)。 |
# -----------------------------------------------
# set -u の基本的な挙動
# -----------------------------------------------
set -u
NAME="五条悟"
echo "術師: ${NAME}"
# 未定義の変数を参照するとエラーになります
echo "学年: ${GRADE}" # ← ここでエラーになります
# デフォルト値を設定して -u のエラーを回避します
echo "等級: ${RANK:-不明}" # ← エラーになりません
$ bash demo_u.sh 術師: 五条悟 demo_u.sh: line 9: GRADE: unbound variable
set -o pipefail の挙動
デフォルトでは、パイプ A | B | C の終了ステータスは末尾コマンド C の終了ステータスだけです。そのため -e を有効にしていても、パイプの途中で失敗したコマンドは見逃されます。set -o pipefail を加えると、パイプライン中で最後に失敗したコマンドの終了ステータスがパイプライン全体のステータスになります。
# ----------------------------------------------- # pipefail なし(デフォルト)の挙動 # ----------------------------------------------- # cat が失敗(ファイルなし)しても、grep が成功すれば $? は 0 になります cat /tmp/no_such_file | grep "五条" echo "終了ステータス: $?" # → 0(失敗を見逃します) # ----------------------------------------------- # pipefail あり # ----------------------------------------------- set -o pipefail cat /tmp/no_such_file | grep "五条" echo "終了ステータス: $?" # → 1(cat の失敗が伝わります)
# pipefail なし $ bash demo_nopipefail.sh cat: /tmp/no_such_file: No such file or directory 終了ステータス: 0 # pipefail あり $ bash demo_pipefail.sh cat: /tmp/no_such_file: No such file or directory 終了ステータス: 1
set -x(xtrace)の挙動
set -x を有効にすると、実行する各コマンドが + プレフィックス付きで標準エラー出力に表示されます。変数は展開済みの値で表示されるため、どの変数にどの値が入っているかも追跡できます。デバッグが終わったら set +x で無効化するか、デバッグしたい区間だけを set -x / set +x で囲むのが一般的です。
# -----------------------------------------------
# set -x の基本的な挙動
# -----------------------------------------------
JUJUTSUSHI="虎杖悠仁"
GRADE="1級"
set -x # デバッグ出力を開始します
echo "術師: ${JUJUTSUSHI}"
echo "等級: ${GRADE}"
set +x # デバッグ出力を終了します
echo "(この行は +x で追跡されません)"
$ bash demo_x.sh + echo '術師: 虎杖悠仁' 術師: 虎杖悠仁 + echo '等級: 1級' 等級: 1級 + set +x (この行は +x で追跡されません)
set -euo pipefail のイディオム
本番用のシェルスクリプトでは、スクリプト先頭付近に set -euo pipefail を1行書くことが推奨されます。これにより「コマンドの失敗」「未定義変数の参照」「パイプ内のエラー」の3つを一括して検知できます。スクリプトの信頼性が大きく向上するため、ほぼすべての本番スクリプトで使われる定番パターンです。
| 組み合わせ | 検知できるエラー |
|---|---|
set -e | コマンド単体の失敗(終了ステータス ≠ 0)を検知します。 |
set -eu | コマンド失敗 + 未定義変数の参照を検知します。 |
set -euo pipefail | コマンド失敗 + 未定義変数 + パイプ内エラーをすべて検知します。最も堅牢な組み合わせです。 |
# ----------------------------------------------- # set -euo pipefail の定番の書き方 # ----------------------------------------------- #!/bin/bash set -euo pipefail # ここからメイン処理を書きます
サンプルコード
jujutsu_set_options.sh
#!/bin/bash
# -----------------------------------------------
# set -euo pipefail の動作確認スクリプト
# 呪術廻戦の術師5人の等級情報を処理します
# -----------------------------------------------
set -euo pipefail
# -----------------------------------------------
# 術師データファイルのパスを設定します
# ファイルが存在しない場合は -e により即終了します
# -----------------------------------------------
DATA_FILE="/tmp/jujutsu_sorcerers.tsv"
echo "=== 呪術廻戦 術師等級チェッカー ==="
echo ""
# -----------------------------------------------
# 術師データを一時ファイルに書き出します
# -----------------------------------------------
cat > "${DATA_FILE}" <<'EOF'
五条悟 特級術師 六眼・無下限呪術
虎杖悠仁 1級術師(仮) 宿儺の器・黒閃
伏黒恵 準1級術師 十種影法術
釘崎野薔薇 1級術師(仮) 芻霊呪法
七海建人 1級術師 十劃呪法
EOF
echo "--- 術師一覧(全員)---"
# -----------------------------------------------
# awk でタブ区切りを整形して表示します
# -o pipefail があるため awk の失敗も検知します
# -----------------------------------------------
cat "${DATA_FILE}" | awk -F'\t' '{printf "名前: %-16s | 等級: %-16s | 術式: %s\n", $1, $2, $3}'
echo ""
# -----------------------------------------------
# 特級術師だけを抽出して表示します
# -----------------------------------------------
echo "--- 特級術師 ---"
TOKUKYUU=$(grep "特級" "${DATA_FILE}" | awk -F'\t' '{print $1}')
echo "${TOKUKYUU}"
echo ""
# -----------------------------------------------
# 人数を集計します
# -----------------------------------------------
TOTAL=$(wc -l < "${DATA_FILE}")
GRADE1=$(grep -c "1級" "${DATA_FILE}")
echo "--- 集計 ---"
echo "登録術師数 : ${TOTAL} 人"
echo "1級以上 : ${GRADE1} 人"
echo ""
# -----------------------------------------------
# 一時ファイルを削除します
# -----------------------------------------------
rm -f "${DATA_FILE}"
echo "=== 処理完了(一時ファイルを削除しました)==="
$ chmod +x jujutsu_set_options.sh $ ./jujutsu_set_options.sh === 呪術廻戦 術師等級チェッカー === --- 術師一覧(全員)--- 名前: 五条悟 | 等級: 特級術師 | 術式: 六眼・無下限呪術 名前: 虎杖悠仁 | 等級: 1級術師(仮) | 術式: 宿儺の器・黒閃 名前: 伏黒恵 | 等級: 準1級術師 | 術式: 十種影法術 名前: 釘崎野薔薇 | 等級: 1級術師(仮) | 術式: 芻霊呪法 名前: 七海建人 | 等級: 1級術師 | 術式: 十劃呪法 --- 特級術師 --- 五条悟 --- 集計 --- 登録術師数 : 5 人 1級以上 : 3 人 === 処理完了(一時ファイルを削除しました)===
jujutsu_debug_x.sh(set -x デバッグ活用例)
#!/bin/bash
# -----------------------------------------------
# set -x を使ったデバッグ活用スクリプト
# 術師の呪力量を計算する処理を追跡します
# -----------------------------------------------
set -euo pipefail
# -----------------------------------------------
# 術師データ(名前と呪力量の初期値)
# -----------------------------------------------
SORCERERS=("五条悟" "虎杖悠仁" "伏黒恵" "釘崎野薔薇" "七海建人")
CURSED_ENERGY=(9900 8500 7800 7600 8200)
echo "=== 呪力量デバッグレポート ==="
echo ""
# -----------------------------------------------
# デバッグ対象の区間だけ set -x で囲みます
# -----------------------------------------------
echo "--- set -x 開始(計算処理を追跡します)---"
set -x
TOTAL=0
COUNT=${#SORCERERS[@]}
for i in $(seq 0 $((COUNT - 1))); do
NAME="${SORCERERS[$i]}"
ENERGY="${CURSED_ENERGY[$i]}"
TOTAL=$((TOTAL + ENERGY))
done
AVERAGE=$((TOTAL / COUNT))
set +x
echo "--- set -x 終了 ---"
echo ""
# -----------------------------------------------
# 集計結果を表示します(+x オフなので追跡されません)
# -----------------------------------------------
echo "術師数 : ${COUNT} 人"
echo "呪力量合計 : ${TOTAL}"
echo "呪力量平均 : ${AVERAGE}"
$ chmod +x jujutsu_debug_x.sh $ ./jujutsu_debug_x.sh === 呪力量デバッグレポート === --- set -x 開始(計算処理を追跡します)--- + TOTAL=0 + COUNT=5 + seq 0 4 + for i in $(seq 0 $((COUNT - 1))) + NAME=五条悟 + ENERGY=9900 + TOTAL=9900 + for i in $(seq 0 $((COUNT - 1))) + NAME=虎杖悠仁 + ENERGY=8500 + TOTAL=18400 + for i in $(seq 0 $((COUNT - 1))) + NAME=伏黒恵 + ENERGY=7800 + TOTAL=26200 + for i in $(seq 0 $((COUNT - 1))) + NAME=釘崎野薔薇 + ENERGY=7600 + TOTAL=33800 + for i in $(seq 0 $((COUNT - 1))) + NAME=七海建人 + ENERGY=8200 + TOTAL=42000 + AVERAGE=8400 + set +x --- set -x 終了 --- 術師数 : 5 人 呪力量合計 : 42000 呪力量平均 : 8400
概要
『シェルスクリプト』の set コマンドは、シェル自体の動作モードをオプションで制御します。set -e はコマンドの失敗を即終了で検知し、set -u は未定義変数の参照をエラーにします。set -o pipefail はパイプ内部の失敗を -e で検知可能にします。set -x はデバッグ時に実行コマンドをトレース出力します。これら3つを組み合わせた set -euo pipefail はスクリプト先頭に書く定番イディオムで、予期しない失敗をスクリプトの早い段階で止める安全策として広く使われています。終了時のクリーンアップ処理を確実に行いたい場合は trap(シグナルハンドラ) と組み合わせると万全です。また、スクリプトの終了ステータスの扱いについては exit / return(終了ステータス) も合わせて参照してください。
記事の間違いや著作権の侵害等ございましたらお手数ですがこちらまでご連絡頂ければ幸いです。