web-dev-qa-db-ja.com

シェルスクリプトでのエラー処理

空のファイルrun_script.shを作成するステップを含むシェルスクリプトrun_script.lckを作成しました。シェルスクリプトがcronjobによって呼び出されるたびに、run_script.lckの存在をチェックします。ロックが存在する場合は、run_script.shがすでに実行されており、まだ終了していないことを示します。 run_script.lckファイルは、プログラムの最後に削除されます。

問題は、シェルスクリプトがクラッシュしたとき、および終了する前にロックファイルを削除することです。

私はこのような行を書きました:

trap "rm -f run_script.lck" EXIT

ただし、次のような望ましくない状態でロックファイルが削除されます。

シェルAでrun_script.shを実行すると、実行され、ロックファイルが作成されました。次に、シェルBで再度実行すると、スクリプトはすでに実行されており、スクリプトは中止されると表示されました。ただし、トラップは、中止されたスクリプトを終了するシェルBからの信号を含むEXIT信号を受信したため、ロックファイルを削除します。また、シェルAのスクリプトはまだ実行中ですが、ロックが削除され、すでに1つのスクリプトが実行されている間に、誰でも別のrun_script.shを再度呼び出すことができます。

これを解決する方法はありますか?

3
lamwaiman1988

Ignacioの答えを説明するために(次のプロトコルを使用します:最初にロックファイルが存在するかどうかを確認してからトラップをインストールします)、次のような問題を解決できます。

$ cat test2.sh
if [ -f run_script.lck ]; then
  echo Script $0 already running
  exit 1
fi
trap "rm -f run_script.lck" EXIT
# rest of the script ...
4
maxschlepzig

ロックの存在を確認しますトラップを設定します。

途中でエラーのトラップを設定し、エラーが発生した場合はshにスクリプトを中止させます(set -e)。例えば

$ cat test.sh
set -e
trap "echo foo" ERR
if [ $# == 1 ]; then
  exit 0
fi
false
$ bash test.sh
foo
$ bash test.sh 1
$

(どこ $#は引数の数です)

スクリプトでは、スクリプトが正常に実行されたときにトラップコマンドを実行する必要があります(つまり、通常のプログラムフローの終了時などにクラッシュは発生しません)。

set -eは、各終了ステータスが等しくないことを意味します0スクリプトの実行を終了します。それと同様に、set -u未定義の変数を(おそらく偶然に)使用するたびに、実行が終了します。

したがって、このアイデアを元のユースケースに移すと、ソリューションは次のようになります。

$ cat test.sh
set -e
trap "rm -f run_script.lck" ERR
if [ -f run_script.lck ]; then
  echo Script $0 is already running
  exit 1
fi
#
# do all the work ...
#
# no error until now
rm -f run_script.lck
4
maxschlepzig