Bashで多重起動防止する
若いのから「バッチの多重起動を防止するのになんでロックディレクトリなんですか?ロックファイルじゃだめなんですか?」と聞かれたので、メモ。
バッチ処理を作成する際、多重起動を防止することがあると思いますが、特にそれがシェルスクリプトで書かれている場合には、ロックディレクトリを作って排他制御を行います。
若いのが言うようにロックファイルでやろうとすると多分こうなるでしょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
#!/bin/bash
if [ ! -e .lock ]; then
touch .lock
else
echo "多重起動はいけないと思います"
exit 1
fi
for i in {1..3}; do
sleep 1
echo "ショリショリ君アイス $i 本目"
done
rm -f .lock
|
コレをバチバチっと2回実行してみると一見多重起動が防止されているように見えます。
1
2
3
4
5
6
7
8
9
10
11
|
$ ./test1.bash &
[1] 215
$ ./test1.bash &
[2] 218
$ 多重起動はいけないと思います
ショリショリ君アイス 1 本目
ショリショリ君アイス 2 本目
ショリショリ君アイス 3 本目
[1]- Done ./test1.bash
[2]+ Exit 1 ./test1.bash
|
この問題点は、ロックチェックとロック処理が同時に処理されていないことにあります。ロックファイルの存在チェックから作成までに時間がかかってしまったらどうなるか、容易に想像できるでしょう。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
#!/bin/bash
if [ ! -e .lock ]; then
#########################################
# あーここで3秒かかってしまったー
sleep 3
#########################################
touch .lock
else
echo "多重起動はいけないと思います"
exit 1
fi
for i in {1..3}; do
sleep 1
echo "ショリショリ君アイス $i 本目"
done
rm -f .lock
|
そうなっちゃいますよねー。
1
2
3
4
5
6
7
8
9
10
11
12
13
|
$ ./test2.bash &
[1] 222
$ ./test2.bash &
[2] 224
$ ショリショリ君アイス 1 本目
ショリショリ君アイス 1 本目
ショリショリ君アイス 2 本目
ショリショリ君アイス 2 本目
ショリショリ君アイス 3 本目
ショリショリ君アイス 3 本目
[1]- Done ./test2.bash
[2]+ Done ./test2.bash
|
で、ロックディレクトリの話になるのですが、ディレクトリを作成するコマンド mkdir
はディレクトリを作成する際にディレクトリが既に存在するとエラーコードを返します。これを使用することで、ロックとチェックを同時に行います。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
|
#!/bin/bash
mkdir .lock > /dev/null 2>&1
if [ $? -ne 0 ]; then
echo "多重起動はいけないと思います"
exit 1
fi
for i in {1..3}; do
sleep 1
echo "ショリショリ君アイス $i 本目"
done
rmdir .lock
|
上書きされたり追加書き込みされたりするファイルと比べ、ディレクトリといふものの性質を考えると、作成処理をロック処理として使えるのは当たり前っちゃー当たり前ですよね。ちなみに、mkdir -p
するとディレクトリが存在してもエラーにならないので注意(?)してください。
余談なのですが、ディレクトリは二重作成できなくて当たり前ということを考えると、これはプログラム言語を超えてロックが可能ということでもあります。主要な言語をすべて調べたわけではないですが、だいたいディレクトリ作成処理はディレクトリが存在するとエラーやfalseを返します。
例)Java
なので、シェルで多重起動防止する際はロックディレクトリを使うといいと思います。もっとこうできるよというものがあればご教示いただけると幸いです。