一部が失敗した場合にシェルスクリプトを終了する方法は?


51

一部が失敗した場合に終了するシェルスクリプトを作成するにはどうすればよいですか?たとえば、次のコードスニペットが失敗した場合、スクリプトは終了するはずです。

n=0
until [ $n -ge 5 ]
do
  gksu *command* && break
  n=$[$n+1]
  sleep 3

回答:


88

1つのアプローチはset -e、スクリプトの先頭に追加することです。つまり(からhelp set):

  -e  Exit immediately if a command exits with a non-zero status.

したがって、いずれかのコマンドが失敗すると、スクリプトは終了します。

または、exit考えられる障害ポイントで明示的なステートメントを追加できます。

command || exit 1

5
私(およびbash wiki)は、(設計により破壊されたIMO)set -e機能を使用するのではなく、人々が適切なエラー処理にいくつかの考えを入れたいと思っています。ただし、ここでは実際には適用されません。OPは、コマンドの実行に5回失敗した後、スクリプトを終了しようとしています。
ステファンシャゼラス

1
@StéphaneChazelas私はあなたがそれが壊れているかどうかについてあなたと議論しません、私はあなたが正しいと確信しています。しかし、OPは「一部が失敗した場合に終了するシェルスクリプトを作成するにはどうすればよいですか?」と尋ねましたが、5回の失敗後に終了する理由は何ですか?
テルドン

質問を解釈できる他の方法は考えられないからです。
ステファンシャゼラス

1
@StéphaneChazelasあなたは正しいかもしれません。文字通りに解釈しました。スクリプトの一部が失敗した場合、スクリプト全体を終了する方法を教えてください。そしてそれset -eが私が知っている唯一の方法です。
テルドン

そのスクリプトsnipetで、引き起こす可能性がコマンドset -eされるsleepbreak特殊組み込みことは、ほとんどのシェルで失敗した場合に終了するには、スクリプトを引き起こすのコマンドifの左側またはそれに&&影響されませんset -en=...場合失敗する可能性がありn、その後読み取り専用であるが、スクリプトが存在しない場合はスクリプトも終了しますset -e)、そのため解釈はほとんどありません。質問の文言が不十分であることに同意します。
ステファンシャゼラス

17

キーワードを使用して、任意の場所でスクリプトを終了できますexit。あなたはまた、例えばまたはスクリプトが失敗したか、他のプログラムに示すために、終了コードを指定することができるexit 1か、exit 2など(慣例により、終了コード0は成功と0が意味の障害よりも何も大きいためですが、また大会、終了で127を超えるコードは、異常終了(信号など)のために予約されています)。

失敗時に終了する一般的な構成は

if [ failure condition ]; then
    exit n
fi

適切なfailure conditionn。ただし、特定のシナリオでは、異なる手順を実行できます。さて、あなたの場合、5回の呼び出しのいずれかがgksu失敗した場合、終了するというあなたの質問を解釈します。1つの方法は、このような関数を使用することです

function try_command {
    for i in 1 2 3 4 5 ; do
        if gksu command ; then
            return 0
        fi
    fi
    exit 1
}

次に、でループを呼び出しますtry_command

質問に対処する方法には、(より)高度な方法または洗練された方法があります。ただし、上記のソリューションは、たとえばステファンのソリューションよりも初心者にとってアクセスしやすいです。


10
attempt=0
until gksu command; do
  attempt=$((attempt + 1))
  if [ "$attempt" -gt 5 ]; then
    exit 1
  fi
done

exitサブシェルで呼び出されない限り、スクリプトを終了します。たとえば、パイプライン内(...)または$(...)パイプラインの一部であるなどの理由で、スクリプトのその部分がサブシェルにある場合、そのサブシェルのみを終了ます。

その場合、サブシェルに加えてスクリプトを終了するにはexit、そのサブシェルの終了を呼び出す必要があります。

たとえば、サブシェルの2つのネストされたレベルがある場合:

(
  life=hard
  output=$(
    echo blah
    [ "$life" = easy ] || exit 1 # exit subshell
    echo blih not run
  ) || exit # if the subshell exits with a non-zero exit status,
            # exit as well with the same exit status

  echo not run either
) || exit # if the subshell exits with a non-zero exit status,
          # exit as well with the same exit status

サブシェルがパイプラインの一部である場合、複雑になる可能性があります。bash特別持つ$PIPESTATUS配列に類似zsh$pipestatusここであなたを助けることができるものを。

{
   echo foo
   exit 1
   echo bar
} | wc -c
subshell_ret=${PIPESTATUS[0]}
if [ "$subshell_ret" -ne 0 ]; then
  exit "$subshell_ret"
fi

3

トラップは、シグナルを受信するとアクションを実行します。

trap "echo EXIT;  exit" 0
trap "echo HUP;   exit" 1
trap "echo CTL-C; exit" 2
trap "echo QUIT;  exit" 3
trap "echo ERR;   exit" ERR
n=0
until [ $n -ge 5 ]
do
  n=$[$n+1]
  echo $n
  sleep 3
done

これを実行して、正常に終了させます。シグナル0でトラップします。

EXIT

もう一度実行し、^ Cで中断します。シグナル2とシグナル0の両方でトラップします。

CTL-C
EXIT

ゼロ以外の終了ステータスはERRでトラップします

ERR
EXIT
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.