【Bash】コマンドの終了ステータスを判定して何かしたい時のイディオム

Bashの話。

コマンドの終了ステータスを判定して何かしたい時、 下記のように書くことがよくある。

output=$(your_kool_command)
if [ $? -ne 0 ]; then
    echo "Oops..."
    exit 1
fi

これ、if文の $? に想定したコマンドの終了ステータス (この例では your_kool_command のそれ) が入っていることをコード的に表現 (保証) できなくて、くっそキモい。

つまり、下記のようにうっかり間に他のコマンド入れてしまうと、このコードは死ぬ。

output=$(your_kool_command 2> /var/log/your_kool_log_err.log)

echo "##### error log #####"
cat /var/log/your_kool_log_err.log

if [ $? -ne 0 ]; then
    echo "Oops..."
    exit 1
fi

かと言って、↓みたいに一行であれこれするのはあまり好きくない。

if output=$(your_kool_command takes \
        --too --many \
        --many \
        --many \
        "args" "and" "opts" \
        "to write in a line. hoge fuga piyo" \
        2> /var/log/your_kool_error_log.log); then
    echo "Oops..."
    exit 1
fi

大体下記のようにして、お茶を濁すものですが、これでも本質的なキモさは残ったまま。

output=$(your_fool_command 2> /var/log/your_fool_log_err.log)
result=$?

echo "##### error log #####"
cat /var/log/your_fool_log_err.log

if [ $result -ne 0 ]; then
    echo "Oops..."
    exit 1
fi

なんとなく海外のお兄さんが書いたDockerエントリポイント用シェルスクリプトみてたら、 この問題に対応するための、良さげなイディオムを見つけた。

そんなに美しくないけど、これなら大分、吐き気が治まるように思う。

        .
        .
        .

    result=0
    output=$("$@" --verbose --help 2>&1 > /dev/null) || result=$?
    if [ ! "$result" = "0" ]; then
        echo >&2 '[Entrypoint] ERROR: Unable to start MySQL. Please check your configuration.'

        .
        .
        .

出典: https://github.com/mysql/mysql-docker/blob/mysql-server/5.7/docker-entrypoint.sh#L37

そんなことより、リダイレクトの捌きかたがエロティックでよい。