sh / Makefile で特定のエラーコードを無視したい


TL; DR

shell スクリプトの場合

# エラーコード 1 を無視
cmd.sh || exit $(($? - 1))
# 複数のエラーコードを無視したい場合 (下記は 1, 2 を無視)
cmd.sh || ( exit $(($? - 1)) ) || ( exit $(($? - 1)) )

Makefile の場合

TARGET:
    # エラーコード 1 を無視
    cmd.sh || exit $$(($$? - 1))
    # 複数のエラーコードを無視したい場合 (下記は 1, 2 を無視)
    cmd.sh || ( exit $$(($$? - 1)) ) || ( exit $$(($$? - 1)) )

追記:こちらの方法ではエラーコードを変えるという荒っぽいことをしてますが、エラーコードを変えたくない場合の方法、コメントいただきました。よかったら、そちらもご覧ください。

はじめに

shell スクリプトや Makefile の処理で、「このエラーは処理を続けてほしいんだけど」みたいなこと、たまにありますよね。

sh だと

cmd.sh || true  # エラーコードが常に 0 になる

Make だと

TARGET:
    # 頭の "-" で、エラーを無視するようになる
    -cmd.sh

とかやると、エラーを無視してくれますが、あらゆるエラーが無視されます。そうなると、想定外のエラー発生時にも処理が進んでしまうので、できれば特定のエラー時だけ無視するようにしたいところです。

そこで

# エラーコード 1 を無視
cmd.sh || exit $(($? - 1))

1 のところを別の数値にすれば、好きなエラーコードを指定することができます。

|| は、前のコマンドがエラーコード 1 以上で終わった場合に、後ろのコマンドを実行するようにします。
$? で直前のコマンドのエラーコードとり、$(( )) で計算ができるので、1 を引いて、それを新しいエラーコードにしています。

これで、もしエラーコードが 1 なら新しいエラーコードが 0 になって、正常とみなされ処理が継続される、その他のエラーコードならやはり非 0 なので、エラーで停止する、という寸法です。

Makefile でも基本的に同じです。$$ としないと sh のコマンドに $ が渡らないので、そこだけ注意です。

TARGET:
    # エラーコード 1 を無視
    cmd.sh || exit $$(($$? - 1))

応用編: 複数のエラーコードを無視したい

複数のエラーコードを正常としたいときも、同じものをつなげていけばOK、、、なのですが、このままだと最初の exit で処理が終わってしまうので ( ) とサブシェルにしてあげましょう。(なお、sh スクリプトの場合に、後ろに処理がある場合も ( ) でくくってあげる必要があります)

# 複数のエラーコードを無視したい場合 (下記は 1, 2 を無視)
cmd.sh || ( exit $(($? - 1)) ) || ( exit $(($? - 1)) )

また、2 つめ以降の $? には、それまでの計算が反映された値が入るので、そこには注意が必要です。上の例の場合、2つめの exit-1 してますが、一つ前ですでに 1 引かれているので、最初のエラーコードが 2 の場合に、ここで 0 となります。

cmd.sh || ( exit $(($? - 1)) ) || ( exit $(($? - 2)) )

とすると、エラーコード 1, 3 を無視することになります。

あとがき

わりとよくあるニーズな気がするのですが、少しググってもページを見つけられなかったので、書いてみました。

「もっとよいやり方があるよ!」という方、ぜひ教えてもらえるとありがたいです