set -eのもとで特定のコマンドの終了ステータスを変数に入れるシェルスクリプトのスニペット

課題編

シェルスクリプトで「あるグローバルな状態を変える操作を行い、その結果をチェックし、状態をもとに戻す」みたいなタスクをするときに「その結果をチェックし」のところでコマンドの終了ステータスを変数に入れて置きたいみたいなことがあります。例えば、次のようなコマンド操作です。

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を返してシェルスクリプトが終了してしまいます。これをシェルスクリプトを終了させるに終了ステータスを得たいというわけです。

解答編

やりかたは色々ありました。

-e の挙動はポータブルではなくシェルによって挙動が違うので set +e での無効化が必要

どれでも正しく動くのでいいと思いますが、 code=$(set +e ; command ; echo $?) はポータブルで比較的メジャーな構文のみ使い、グローバルの状態に依存したりグローバルの状態を一時的にでも変えたりせず、意図が明確なので今回はこれでいこうかと思います。