シェルスクリプトでset -eしているときに処理を中断せずエラーを扱う方法

シェルスクリプトで set -e しておくと、スクリプト中に実行したコマンドの戻り値が非0だった時、つまりコマンドが失敗したときにそこでスクリプトを中断してくれるようになります。(ここでいう戻り値とは「Exit code」「Return status」などと呼ばれるものです。)

都度エラー判定を書かなくてもよくなるので便利なオプションですが、個別にエラーハンドリングをしたくなった時にも意図せずスクリプトが中断されてしまい困ったことになります。

以下の例では、false コマンドの戻り値は常に1(失敗)でそこでスクリプトが中断されるため “finish” が出力されることはありません。

#!/bin/bash -e
false
# 以降のコマンドは実行されない
echo "finish"

-eオプションで困ってしまうケースの例

例えば grep コマンドで検索結果が見つからなかった場合にエラーメッセージを出力したいケースがあったとします。grep は検索結果が何か見つかれば0(成功)、見つからなければ1(失敗)の戻り値が返るという仕様です。

以下の例では grep で見つからなかった場合、そこでスクリプトが中断されてしまうため、以降の if ブロックが実行されることはありません。

#!/bin/bash -e
grep "apple" hoge.txt >/dev/null
ret=${?}

if [[ $ret != 0 ]]; then
  # ここに入ってくることはない!
  echo "apple を含む行が見つかりませんでした。" 1>&2
  exit 1
fi

これについて対処法を4つメモしておきます。

対処法1: コロンコマンドを利用する (:コマンド)

#!/bin/bash -e
grep "unmatch pattern" hoge.txt && :
echo "return value: ${?}"

→ return value: 1

解説

これは -e オプションにおける特殊な特性を利用した方法です。
: は一つの built-in command で、何もせずに0(成功)の戻り値を返すだけのコマンドです。

式の評価の流れ:

  • grep の戻り値が0(成功)だった場合、&& 演算子により右側も評価され : を実行。一連のコマンドの戻り値は0(成功)。
  • grep の戻り値が1(失敗)だった場合、&& 演算子によりそこで評価を終了。なので一連のコマンドの戻り値は1(失敗)。

更に set -e オプションには && や || で結合された式リストの最後にあるコマンドが1(失敗)だった場合のみスクリプトを中断するという特性があるため、そのままスクリプトを続行できます。$? にもちゃんと grep の戻り値が入っています。

そのためちょっと直感的ではないですが、以下のコマンドの出力は “route1″のみ となります。

#!/bin/bash -e
false && false
echo "route1"

false
echo "route2"

対処法2: trueコマンドを利用する

grep "unmatch pattern" hoge.txt && true
echo "return value: ${?}"

→ return value: 1

解説

対処法1と同じ理由です。true コマンドも : と同じく何もせず常に0(成功)を返すコマンドです。

対処法3: 戻り値を取得せずifでそのまま判定する

#!/bin/bash -e
if ! grep "apple" hoge.txt >/dev/null; then
  echo "Not found apple..." 1>&2
fi

解説

この方法は一番シンプルに解決できる方法かもしれません。
こちらも-eオプションにおける挙動の特性を利用しており、if の評価式部分のコマンドに関しては失敗しても中断されないという特性があるため、スクリプトを続行できます。

対処法4: 一時的に set +e して -e オプションを解除する

#!/bin/bash -e
set +e
grep "unmatch pattern" hoge.txt
echo "return value: ${?}"
set -e

→ return value: 1

解説

個人的に少しダサさを感じてしまいますが、この方法が楽そうならこれで。最後に set -e で元に戻すのを忘れずに。

感想

対処法3のケースなど普段何気なく実現できていたことも、ちょっとした挙動の特性により実現出来ていたことが理解できました^^
個人的にはコロンコマンドを使用した方法がシンプルで好きですが、時と場合で使い分けたいと思います。

参考資料

編集履歴

  • 2018/11/22: 全体的に例や内容をブラッシュアップしました。

3 thoughts on “シェルスクリプトでset -eしているときに処理を中断せずエラーを扱う方法

  1. 大変参考になりました。ありがとうございます。
    下記のコマンドの存在チェックで困っていたので、対処法2を使います。

    type command >/dev/null 2>&1

コメントを残す

メールアドレスが公開されることはありません。

計算問題(認証) *