bash の trap で書く後始末と終了コード

結論 trap は渡した文字列を登録し、発火した時点でコードとして読み直す。単引用符なら変数は発火時の値になり、二重引用符なら張った時点の値が構文の一部として埋まる EXIT trap が発火するのはシェルの終了時で、張った関数の中ではない。その関数の local 変数はもう無いので、値は printf -v q '%q' で埋めるか、グローバル変数に置く EXIT trap は SIGINT・SIGTERM・SIGHUP で止められても走り、SIGKILL では走らない。trap cleanup EXIT INT と INT も並べると、Ctrl-C の後もスクリプトが先へ進む trap はシェルに 1 つの表で、関数ごとの範囲を持たない。ライブラリの trap - EXIT は呼び出し元の EXIT trap も消し、関数で張った RETURN trap は呼び出し元の return でも発火する set -e の下では、EXIT trap の中の失敗が終了コードを書き換え、trap の残りの行を飛ばす。set -u だけなら trap は止まるが、終了コードは残る 以下は GNU bash 5.3.20 / Linux x86_64 で実測した。 前提: trap は「後で読み直す文字列」をシェルの表に登録する trap '<文字列>' EXIT は、その場では何も実行しない。文字列をシェルの表に登録し、発火したときに新しいコマンドとして読み直す。 表はシェル (プロセス) に 1 つで、シグナル (EXIT・RETURN を含む) ごとに 1 本の文字列を持つ。同じシグナルに張り直すと、前の文字列は置き換わる 登録した文字列は trap -p <シグナル> で読める 張った時点と発火した時点では、変数の値も、呼び出し中の関数も、表の中身も変わりうる。以下の罠はどれもこのずれから起きる set -e 自体の効き方は bash の set -e が効かない場所と効きすぎる場所 に、 ERR trap がパイプの失敗で発火しない件は trap コマンドは pipe と併用すると動作しない に書いた。 ...

2026-11-05 ·  2026-11-05 · 9 分 · 1783 文字

bash のパイプが返す終了コードと SIGPIPE

結論 パイプの終了コードは、既定では末尾のコマンドのもの。set -o pipefail を立てると、最後に非 0 で終わったコマンドのものになる || と if はパイプ全体の終了コードを見る。cmd | grep -v X || true は cmd の失敗も 0 にし、if cmd | grep -q X は取得の失敗を「一致なし」と同じ偽にする 読み手が終わった後に書き手が書くと、書き手は SIGPIPE で 141 になり、pipefail がそれを全体の終了コードにする。| head、| grep -q、stdin を読まない読み手で起き、出力が小さくても書く順番しだいで起きる mapfile < <(cmd) はパイプではないので、cmd の失敗はどこにも残らない 判定に使う出力は先に変数へ取り、here-string で渡す。fallback 付きのコマンドは、成功したときだけ stdout に出す 以下は GNU bash 5.3.15 / GNU coreutils 9.11 / GNU grep 3.12 / Linux x86_64 で実測した。回数を書いたものはタイミングで決まるので、環境によって数字が変わる。 前提: パイプの終了コードはどのコマンドのものか ...

2026-10-27 ·  2026-10-27 · 12 分 · 2495 文字

スクリプトから呼ぶ fzf が返す値と終了コード

結論 --select-1 は候補が 1 件なら、--exit-0 は 0 件なら、一覧を出さずに終わる。「fzf で人が選ぶ」前提の導線でも、1 件に絞れた候補はそのまま確定する --query の既定はあいまい一致で、行全体を照合する。api は apps-migration に当たり、--exact でも main は domain-model に当たる。行と完全に一致させるのは ^main$ だけ fzf は --filter 以外では起動時に /dev/tty を開く。制御端末の無いプロセスでは、--select-1 で 1 件に絞れても failed to open /dev/tty で exit 2 になる 終了コードは 0 (選んだ) / 1 (一致なし) / 2 (エラー) / 130 (中断)。--expect= (キー無し) は 2。producer | fzf は、fzf が先に終わると producer が SIGPIPE の 141 で終わり、pipefail で全体が 141 になる {} {1} はシングルクォートで囲んだ 1 引数に展開される。自分で "{}" と囲むと、そのクォートが値に混ざる preview は行ごとに別の $SHELL -c で走り、呼び出し元の関数と export していない変数は見えない --multi の出力は選んだ順。--tac は入力の並びを反転するだけで、カーソルは常に並びの先頭に乗る 以下は fzf 0.74.3 / bash 5.3.15 / linux/amd64 で実測した。対話の操作は script コマンドが作る疑似端末 (pty) の中で fzf を動かし、キーを送って再現した。 ...

2026-10-16 ·  2026-10-16 · 8 分 · 1549 文字

bash の set -e が効かない場所と効きすぎる場所

結論 set -e は「条件の文脈の外で、コマンドが非 0 を返したら止まる」だけの仕組みで、失敗を報告はしない 効きすぎる: 関数の戻り値、x=$(cmd) の代入、((n++))、wait、read が非 0 を返すと、メッセージ無しでその行で終わる 効かない: && / || の途中、if / while の条件、そこから呼んだ関数と subshell の中、$( ) の中、set -e より前の行 失敗の扱いは set -e 任せにせず、その文に || return 1 や || fallback を付けて明示する 以下のコードはすべて GNU bash 5.3.15 で実測した。 前提: set -e が止めない文脈 bash は、失敗したコマンドが次のどれかに当たるとき set -e で終了しない。 while / until の直後のコマンドリスト if / elif の条件 && / || のリストの途中 (最後以外) ! で反転したコマンド この免除は、その文脈で実行される関数本体と subshell の中まで及ぶ。 逆に、この文脈の外で非 0 を返した文は、関数呼び出しでも代入文でも止まる。 ...

2026-09-28 ·  2026-09-28 · 5 分 · 981 文字