空のファイル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
を再度呼び出すことができます。
これを解決する方法はありますか?
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 ...
ロックの存在を確認します前トラップを設定します。
途中でエラーのトラップを設定し、エラーが発生した場合は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