課題編
シェルスクリプトで「あるグローバルな状態を変える操作を行い、その結果をチェックし、状態をもとに戻す」みたいなタスクをするときに「その結果をチェックし」のところでコマンドの終了ステータスを変数に入れて置きたいみたいなことがあります。例えば、次のようなコマンド操作です。
set -e # グローバルな状態を変える操作を行う git merge --no-ff --no-commit $main_branch || true # 結果をチェックしてexit codeを変数に入れる git diff --cached --exit-code --quiet ; code=$? # グローバルな状態をもとに戻す git merge --abort # 上位プロセスに結果を渡す exit $code
スクリプト全体には set -e (コマンドが失敗するとシェルスクリプトが即座に終了する)を効かせていると、このままだと git diff --exit-code ... で差分があるときに終了ステータス1を返してシェルスクリプトが終了してしまいます。これをシェルスクリプトを終了させすに終了ステータスを得たいというわけです。
解答編
やりかたは色々ありました。
興味がわいたので検索してみたら、一時的に set +e して set -e で戻すしかないみたいですね。https://t.co/lib3MOADLh
— Hiroaki Nakamura (@hnakamur2) 2021年12月15日
set -e
— mattn (@mattn_jp) 2021年12月15日
{ something-command; CODE=$?; } || :
echo "$CODE"
が良い気がします。
じつは&&trueをつければ終了しない。
— eban (@eban) 2021年12月15日
set -e
false && true
echo $?#シェル芸
ex=0
— Koichi Nakashima (@ko1nksm) 2021年12月15日
cmd || ex=$?
とか
cmd && : # または cmd && true
ex=$?
個人的には if cmd; then のように書くので、終了ステータスの値を本当に参照したい時以外はシェル変数に入れたりしませんが
ポータブル(sh / bash / zshで動く)、簡単な構文、やりたいことが明白、という観点から検討した結果以下のやりかたはぼくは好みです。
— FUJI Goro (@__gfx__) 2021年12月15日
code=$(set +e ; command ; echo $?)
※ -e の挙動はポータブルではなくシェルによって挙動が違うので set +e での無効化が必要
どれでも正しく動くのでいいと思いますが、 code=$(set +e ; command ; echo $?) はポータブルで比較的メジャーな構文のみ使い、グローバルの状態に依存したりグローバルの状態を一時的にでも変えたりせず、意図が明確なので今回はこれでいこうかと思います。