シェルスクリプトで排他制御したいケースがたまにあります。
「多重起動の抑制」「二重起動の禁止」などとされることもあります。よくやるのは pid ファイルを作成したりロックファイルを作成したりだと思いますが、「ファイルの有無」とか「プロセスIDのチェック」がどうも不安定に感じていました。ファイルシステムの排他ロックとか簡単に使えればいいのに…!
調べてみると、flock(1) なんていう便利なものがあることがわかりました。
第二版
あえて、「singleton.sh を source することでスクリプトを排他制御する」という構成にこだわる。
# 利用者はこのファイルを source します
_LOCK_FILE=$0.lock
exec {F_LOCK}>>$_LOCK_FILE
flock -w 1 ${F_LOCK}
if [ $? -ne 0 ]
then
echo "maybe already running.">&2
exit 1
fi
コメントでいただいた指摘通り、やはりロックファイルを作成して、終わったら消す方式では安定した排他制御は望めない。どうしても、「ロック解放」から「削除」までのタイムラグが発生してしまう。
しかたがないので、ロックファイルを置きっぱなしにすることにする。(悔しい)
利用側は以下のように。
#!/bin/sh
source ./singleton.sh
echo "hogehoge"
sleep 2
echo "fugafuga"
自分自身をオープンしちゃう方法と問題点
たぶん自分自身をオープンしておくやり方の方がロックファイルより確実だと思います。
ただ、シェルスクリプトを実行する際、そのファイルが書き込み用にオープンされていると気持ち悪い感じで失敗します。
#!/bin/sh
exec {F_LOCK}>>$0
sleep 10
echo "ok"
こいつを平行して実行しようとすると、
[vagrant@localhost ~]$ ./app2.sh &
[2] 5762
[vagrant@localhost ~]$ -bash: ./app2.sh: /bin/sh: bad interpreter: テキストファイルがビジー状態です
こうなるのです。
一瞬これだけで排他制御できるじゃんと思いましたそううまい話でもなく、スクリプトを起動する瞬間しか排他効果がないので、exec によってオープンされるまでの間に素早く起動させれば余裕で平行実行可能です。
注意事項
- 上のモジュール自体はネタです。
- flockコマンドは Util-Linux のものです。Macでは動きませんし、古いバージョンのディストリビューションでも入ってません。