はじめに
cronで定期的にコマンドやシェルスクリプトを実行する場合、
- そのコマンドやシェルスクリプトが応答を返さない
- 処理が遅く次の定期実行タイミングまでに終わらない
という事象が発生する可能性があり、そうなった場合はcronで次に予定されている実行を防止する必要があります。
この防止がない場合、cronは予定されている間隔で無条件(前回の実行状態を加味せず)にコマンドやシェルスクリプトを実行していきますので、その分プロセスが残っていき、最後にはメモリ不足が発生しサーバーの応答がなくなるなどの問題に繋がる場合があります。
今回はシェルスクリプト+ロックファイルでその防止(二重実行防止)を実現するやり方を紹介します。
また、今回の二重実行防止策は、
- 定期実行の後処理でいろいろなことをしたい方!
- 引き継いだサーバー+定期処理で再起動時にロックファイルが残ってしまう事象に遭遇している方!
そんな方々のお役にも立てられるかもしれません。
では本題です!
シェルスクリプト例
#!/bin/bash
LOCKFILE="/var/run/my-script.lock"
if [ -f $LOCKFILE ]; then
echo "Already execute script."
exit 9
fi
trap "{
rm $LOCKFILE;
}" EXIT
touch $LOCKFILE
#ここにやりたいことを書く
簡単ですね。
touchコマンドで特定の位置(/var/run)にロックファイル(my-script.lock)を生成し、次の実行でそのロックファイルが存在している==前に実行したシェルスクリプトが実行中と判断し中断する、というものです。
以降で、もう少し詳しく見ていきましょう。(trapはなに???ということも含めて)
LOCKFILE="/var/run/my-script.lock"
ロックファイルのパスを変数に格納します。
/var/runにはプロセスIDを記載した.pidファイルが格納されますが、今回は/var/runに作成するようにしました。ここは任意のフォルダやファイル名で問題ありません。
ただし書き込み権限があるフォルダにする必要がありますので、その点だけ注意してください。
if [ -f $LOCKFILE ]; then
ロックファイルの存在をチェックしています。
-fは"ファイルが存在するか"になります。
-eで"パスが存在するか"になりますが、そちらでも問題ありません。
echo "Already execute script."
exit 9
ロックファイルが存在していれば、エラーを出して終了しています。上記の場合の終了コードは9になりますが何番でもかまいません。(0でも可)
スクリプトの呼び元で終了コードを判定する場合に適切な終了コードを指定してください。
trap "{
rm $LOCKFILE;
}" EXIT
これがこの記事の肝になります。(私的に)
一見、
\#ここにやりたいことを書く
rm $LOCKFILE;
とスクリプトの最後でも問題ないように思います。
しかしながら "#ここにやりたいことを書く"で書いたことが応答を返さないなど正常に終了しないケースもあり、Ctrl+C で中断したりプロセスをkillしたりしますが、その場合はロックファイルが削除されません。(実行中にサーバーを再起動した場合も同じですね)
trapを利用することで、Ctrl+Cでの中断やプロセスkillのシグナルを受信(トラップ)することができ、任意の処理(ここではrm)を行うことができます。シグナルについてはここでは省きます。
touch $LOCKFILE
touchはタイムスタンプを変更するコマンドですが、存在しないファイルを指定した場合は空(ファイルサイズが0)のファイルを作成します。今回の二重実行防止ではファイルが空であれ存在しているかどうか、が判断できればよいので、ロックファイル生成の目的でtouchを利用しています。
その他の後処理
今回、
trap "{
rm $LOCKFILE;
}" EXIT
ではrmでのロックファイルを削除するだけになっていますが、それ以外の後処理も記載することが可能です。
以下はあくまでも例ですが、
- その他不要プロセスのkill
- その他残骸ファイルの削除
- LED消灯
- メール通知
- 某所への投稿
などなど。
※何がどこまでできるかは私自身も把握しておりません。例ということでご理解ください。
その他の二重実行防止
今回のシェルスクリプト(touch、trap、rm)を利用したやり方以外にも、
- 各言語のシグナル処理を利用する。
- ps、grepを利用する。
などがあります。
各言語にはシグナル処理があり、そちらでシグナルを受信し任意の処理を実行することも可能です。
ただ引き継いだソースにシグナル処理がなく、手っ取り早く対応したいなんて時は今回のやり方で対応してみては如何でしょう。
またps、grepを利用する方法については以下の記事で書きました。あわせてご確認ください!
ps、grepを利用して二重実行防止 - Qiita
https://qiita.com/SECUAL_masa/items/f402546bca49c4943e7e
他にこんなやり方があるよ!って方はぜひコメントいただけると幸いです。