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

なので、シェルで多重起動防止する際はロックディレクトリを使うといいと思います。もっとこうできるよというものがあればご教示いただけると幸いです。