はじめに
以下の記事でps、grepを利用した二重実行防止について触れましたので、この記事で詳しく書いていこうと思います。
シェルスクリプト+ロックファイルで二重実行防止 - Qiita
https://qiita.com/SECUAL_masa/items/5b1b7e8c4ed13c420435
ps、grepを利用した二重実行防止のコマンド例は以下になります。
コマンド例
ps ax | grep my-script.sh | grep -v grep || /path/my-script.sh
cronに書くと、
*/1 * * * * ps ax | grep my-script.sh | grep -v grep || /path/my-script.sh
のようになります。
日本語で説明すると、
1分間隔で実行中のプロセスの中にmy-script.shが存在するかを確認し、存在していれば実行しない。
存在しなければ実行する。
となり、二重実行防止になります。
では、「コマンド例」の内容をもう少し詳しく見ていきましょう。
、、、のその前に、my-script.shについても少し。
$ cat my-script.sh
#!/bin/bash
sleep 10
前回の"my-script.sh"とは異なり、単に10秒だけsleepするスクリプトになっています。
・実行後の10秒以内がコマンド/スクリプト実行中
・未実行or10秒経過後がコマンド/スクリプト未実行
として説明していこうと思います。
「コマンド例」の詳細説明
ようやく本題です。
ps ax | grep my-script.sh | grep -v grep
パイプ("|"の文字)の詳細は省きますが、
- psでLinux上で実行されているプロセスの一覧を表示し、
- その中からgrepで"my-script.sh"を検索する。
- 検索結果にgrep自身もヒットするので、それは除外(-v)する。
という意味になります。
"ps ax | grep my-script.sh"の結果と"ps ax | grep my-script.sh | grep -v grep"で違いを比較してみてください。(grepは"my-script.sh”でなくてもOKです。ntpやsshなど)
次にgrepの実行結果を見ていきます。
以下の結果を比較してみてください。
$ ./my-script.sh &
[1] 313163
$ ps ax | grep my-script.sh | grep -v grep
313163 pts/0 S 0:00 /bin/bash ./my-script.sh
$ echo $?
0 ・・・ 実行結果
$ ps ax | grep my-script.sh | grep -v grep
$ echo $?
1 ・・・ 実行結果
シェルで"$?"には"直前に実行したコマンドの実行結果"が入ります。
"echo $?"で、その実行結果を表示しています。
前回の記事でも終了コードについて触れましたが、psやgrepなどの一般的なコマンドの終了コードは、0が正常終了(成功)、1が異常終了(失敗)を意味し、またgrepでは指定した文字列("my-script.sh")が
・ヒットしたら正常終了(成功)
・ヒットしなかったら異常終了(失敗)
となります。
次の
||
は、左辺のコマンド実行が失敗した場合に右辺のコマンドを実行する、という意味になります。(逆に"&&"は、左辺のコマンド実行が成功したら右辺のコマンドを実行する、という意味になります)
if文で利用する論理和(||)とは挙動が逆転しているかのような感覚になるので注意が必要です。(後で説明します)
よって、「コマンド例」は、
・左辺のgrepで"my-script.sh"がヒットしなかったら(失敗したら)、/path/my-script.sh を実行する。
・左辺のgrepで"my-script.sh"がヒットしたら(成功したら)、/path/my-script.sh は実行しない。
という意味になり、二重実行を防止するということに繋がります。
応用編
応用でプロセス監視にも利用可能です。
*/1 * * * * ps ax | grep ntpd | grep -v grep || /etc/init.d/ntpd restart
実行されているはずのntpdが存在しなければntpdを起動する、という使い方もできます。
論理和としての||
少しだけ寄り道します。論理和としての||の説明です。
逆に混乱させてしまうかもしれませんので、読み飛ばしていただいた方が良いかもしれません。
if ( 式A || 式B ) {
}
上記のロジックでは、
- "式A" の評価結果が"真(true)"なら式Bは評価せずthenルートに入る。
- "式A" の評価結果が"偽(false)"なら式Bを評価し、式Bの評価結果が"真(true)"ならthenルートに入る。
という動きになります。(いきなり"thenルート"という表現が出てきましたが、ここでは説明を省略します。"if then else"辺りで検索してみてください)
言語にもよりますが、0以外は"真(true)"、0は"偽(false)" と同意でもあります。
ですので「コマンド例」の場合、論理和を基準に考えると、
・grepで"my-script.sh"がヒットした場合(実行結果が0)に/path/my-script.shを実行する。
・grepで"my-script.sh"がヒットしなかった場合(実行結果が1)は/path/my-script.shを実行しない。
と、逆の意味で理解してしまいそうなので注意してください。