導入
シェルスクリプトの既定の動きは「1行失敗しても、次の行を実行する」です。バックアップに失敗したのに、その次の「古いファイルを削除」だけが成功する——考えるだけで恐ろしい挙動です。これを変えるのが set です。
説明
スクリプトの冒頭に書く「おまじない」の正体を理解しましょう。
#!/bin/bash
set -e
set -u
echo "1: 開始します"
# 失敗するコマンド(存在しないファイル)
cat noexist.txt
echo "2: ここは実行されません"
| 指定 | 意味 | 防げる事故 |
|---|---|---|
set -e | コマンドが失敗したらそこで終了 | 失敗を無視して次に進む |
set -u | 未定義の変数を使ったらエラー | rm -rf $TYPO/ が rm -rf / になる |
set -o pipefail | パイプの途中の失敗も検知する | grep x file | wc -l の失敗を見逃す |
set -x | 実行した行を表示する | デバッグ用(レッスン33) |
3つまとめて set -euo pipefail と書くのが、現代のシェルスクリプトの定型句です。
flowchart TD
A["cat noexist.txt が失敗"] --> B{"set -e は?"}
B -->|"なし(既定)"| C["次の行へ進む<br/>(危険)"]
B -->|"あり"| D["その場でスクリプト終了<br/>(安全)"]
set -u の効果も見ておきましょう。変数名を打ち間違えたとき、既定では空文字として黙って動いてしまうのが、シェルの怖いところです。
set -u
echo "対象: $TAGET" # TARGET の打ち間違い → エラーで止まる
注意点として、set -e は「失敗してもよい」場面と相性が悪いことがあります。grep が何も見つけられないと終了ステータス 1 を返すので、そこで止まってしまうのです。意図的に無視したいときは || true を付けます。
count=$(grep ERROR logs/app.log | wc -l || true)
やってみよう
スクリプトを実行して、2: ここは実行されません が出ないことを確かめましょう。次に set -e の行をコメントアウト(先頭に #)して実行すると、エラーのあとも 2: が表示されます。この差が事故と無事故の分かれ目です。
演習
set -e を有効にしたまま、失敗するコマンドの後ろに || true を付けて、最後まで到達しました を表示させてください。
ヒント1を見る
cat noexist.txt 2> /dev/null || true のように書くと、失敗しても止まりません。
ヒント2を見る
その次の行に echo "最後まで到達しました"