web-dev-qa-db-ja.com

Bashスクリプトで、特定の条件が発生した場合にスクリプト全体を終了する方法

私はいくつかのコードをテストするためにBashでスクリプトを書いています。しかし、最初にコードのコンパイルが失敗した場合、テストを実行するのは馬鹿げているように見えます。その場合は、テストを中止します。

スクリプト全体をwhileループの内側にラップし、ブレークを使用せずにこれを実行できる方法はありますか? dun dun dun gotoのようなものですか?

565
samoz

この文を試してください:

exit 1

1を適切なエラーコードに置き換えます。 特別な意味を持つ終了コード も参照してください。

624

set -e を使用

#!/bin/bash

set -e

/bin/command-that-fails
/bin/command-that-fails2

スクリプトは失敗した最初の行の後で終了します(ゼロ以外の終了コードを返します)。この場合、 command-that-fails2 は実行されません。

すべてのコマンドのリターンステータスを確認すると、スクリプトは次のようになります。

#!/bin/bash

# I'm assuming you're using make

cd /project-dir
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

cd /project-dir2
make
if [[ $? -ne 0 ]] ; then
    exit 1
fi

set -e を指定すると、次のようになります。

#!/bin/bash

set -e

cd /project-dir
make

cd /project-dir2
make

失敗したコマンドはスクリプト全体を失敗させ、終了ステータスを返します。 $?で確認できます。 。スクリプトが非常に長い場合や、たくさんのものを作成している場合は、どこにリターンステータスチェックを追加しても、かなり醜くなります。

623
Shizzmo

悪いお尻のSysOpsの男がかつて私にThree-Fingered Clawのテクニックを教えてくれました:

yell() { echo "$0: $*" >&2; }
die() { yell "$*"; exit 111; }
try() { "$@" || die "cannot $*"; }

これらの機能は* NIX OSとシェルのフレーバーロバストです。それらをあなたのスクリプトの先頭(bashかそれ以外)に置き、try()あなたのステートメントとコードを書いてください。

説明

飛んでいる羊に コメント)。

  • yellstderr:へのスクリプト名とすべての引数を表示します。
    • $0はスクリプトへのパスです。
    • $*はすべて引数です。
    • >&2>は標準出力を&pipe 2にリダイレクトします。 pipe 1はそれ自身stdoutになります。
  • dieyellと同じように機能しますが、 0以外の終了ステータス で終了します。これは「失敗」を意味します。
  • try||(boolean OR)を使います。これは左側が失敗しなかった場合にのみ右側を評価します。
    • $@もまたすべての引数ですが、 異なる です。

それがすべてを説明してくれることを願っています。

182
c.gutierrez

sourceを指定してスクリプトを呼び出す場合は、return <x>を使用できます。ここで<x>はスクリプトの終了ステータスになります(errorまたはfalseにはゼロ以外の値を使用します)。スクリプトをsourceにしても(つまり、直接そのファイル名で)実行しても、これは期待通りに動作しますが、return文は文句を言うでしょう(エラーメッセージ "return:return"は 'からのみ' '関数またはソーススクリプト ")。

代わりにexit <x>を使用した場合、スクリプトがsourceを指定して呼び出されると、スクリプトを開始したシェルが終了しますが、実行可能スクリプトは直接正常に実行されます。

どちらの場合も同じスクリプトで処理するには、次のようにします。

return <x> 2> /dev/null || exit <x>

どちらの呼び出しが適していても、これが処理されます。

注:<x>は単なる数字になっています。

23
kavadias

私はしばしばエラーを処理するためにrun()と呼ばれる関数を含みます。私がしたいすべての呼び出しはこの関数に渡されるので、失敗したときにスクリプト全体が終了します。 set -eソリューションよりも優れているこの利点は、行が失敗したときにスクリプトがサイレントモードで終了しないことです。そして、問題が何であるかを知ることができます。次の例では、falseの呼び出しでスクリプトが終了するため、3行目は実行されません。

function run() {
  cmd_output=$(eval $1)
  return_value=$?
  if [ $return_value != 0 ]; then
    echo "Command $1 failed"
    exit -1
  else
    echo "output: $cmd_output"
    echo "Command succeeded."
  fi
  return $return_value
}
run "date"
run "false"
run "date"
10
Joseph Sheedy

if構文の代わりに、 短絡評価 を利用することができます。

#!/usr/bin/env bash

echo $[1+1]
echo $[2/0]              # division by 0 but execution of script proceeds
echo $[3+1]
(echo $[4/0]) || exit $? # script halted with code 1 returned from `echo`
echo $[5+1]

交代演算子の優先順位のために必要な括弧のペアに注意してください。 $?は、最近呼び出されたコマンドの終了コードに設定された特別な変数です。

5
skalee

私は同じ質問をしていますが、それは重複しているのでそれを尋ねることはできません。

スクリプトがもう少し複雑な場合、exitを使用して受け入れられた答えは機能しません。バックグラウンドプロセスを使用して状態をチェックする場合、exitはサブシェルで実行されるため、そのプロセスのみを終了します。スクリプトを強制終了するには、明示的にそれを強制終了する必要があります(少なくともそれが私が知る唯一の方法です)。

これを実行する方法についての小さなスクリプトです。

#!/bin/bash

boom() {
    while true; do sleep 1.2; echo boom; done
}

f() {
    echo Hello
    N=0
    while
        ((N++ <10))
    do
        sleep 1
        echo $N
        #        ((N > 5)) && exit 4 # does not work
        ((N > 5)) && { kill -9 $$; exit 5; } # works 
    done
}

boom &
f &

while true; do sleep 0.5; echo beep; done

これはより良い答えですが、それでも不完全なものです/ ブーム の部分を取り除く方法が本当にわかりません。

0
Gyro Gearloose