kun432's blog

Alexaなどスマートスピーカーの話題中心に、Voiceflowの日本語情報を発信してます。たまにAWSやkubernetesなど。

〜スマートスピーカーやVoiceflowの記事は右メニューのカテゴリからどうぞ。〜

シェルスクリプトでロック

シェルスクリプトでロックファイルを使った排他制御をする場合、普通にファイルだとうまく行かない場合がある。なぜならロックファイルの作成と確認がアトミックに行われないから。

でこれを回避する方法として、シンボリックリンクを使う方法が紹介されているけど、他にも色々あるみたいなので試してみた。

シンボリックリンク

とりあえず上記でも紹介されているシンボリックリンクを使うやり方。

#!/bin/bash

LOCK_FILE=./file.lock

## ロックファイルの確認と作成
if ! ln -s $$ $LOCK_FILE; then
    echo "LOCKED"
    exit 0
fi

## メインの処理
for ((i=0;$i<10;i=$i+1)); do
    echo $i
    sleep 1
done
echo OK

## ロックファイルの削除
rm -f $LOCK_FILE

exit 0

2つのターミナルで実行するとこうなる。

$ ./lock.sh
0
1
2
...(snip)...
7
8
9
OK
$ ./lock.sh
ln: ./file.lock: File exists
LOCKED

実行中は以下の通りシンボリックリンクが張られている。実際には実体が存在しないリンクになってるけど、問題ない。

$ ls -lt 
lrwxr-xr-x  1 xxxxx  xxxxx    5  7  6 02:16 file.lock -> 28128

ディレクトリを使う

使い方としてはどうよ?と思わないでもないけど、ディレクトリでもよい。個人的には昔はこれをよく使っていた。

#!/bin/bash

LOCK_DIR=./lock

## ロックファイルの確認と作成
if ! mkdir $LOCK_DIR; then
    echo "LOCKED"
    exit 0
fi

## メインの処理
for ((i=0;$i<10;i=$i+1)); do
    echo $i
    sleep 1
done
echo OK

## ロックファイルの削除
rmdir $LOCK_DIR

exit 0

実行結果

$ ./lock.sh
0
1
2
...(snip)...
7
8
9
OK
$ ./lock.sh
mkdir: ./lock: File exists
LOCKED

lockfileコマンド

procmailに含まれているlockfileコマンドを使う。個人的にこれは知らなかった。

Macの場合はhomebrewのprocmailパッケージに含まれている。

$ brew install procmail
$ which lockfile
/usr/local/bin/lockfile

スクリプトはこんな感じ。

#!/bin/bash

LOCK_FILE=./file.lock

## ロックファイルの確認と作成
if ! lockfile $LOCK_FILE; then
    echo "LOCKED"
    exit 0
fi

## メインの処理
for ((i=0;$i<10;i=$i+1)); do
    echo $i
    sleep 1
done
echo OK

## ロックファイルの削除
rm -f $LOCK_FILE

exit 0

これの面白いところは、オプションを指定せずに実行すると、ロックファイルが作れるようになるまで待つこと。

$ ./lock.sh
0
1
2
...(snip)...
7
8
9
OK
$ ./lock.sh
(1つ目の実行が終わるまでは停止したまま、1つ目の実行が終わると処理が進む)
0
1
2
...(snip)...

オプションでは、

  • 何秒間隔で何回リトライする
  • ロックファイルのタイムスタンプから何秒経っていたら強制的に削除する

というようなことができて、例えば以下のように指定すると、1秒間隔で2回リトライになる。

if ! lockfile -1 -r 2 $LOCK_FILE; then

ロック中はこんな感じになる。

$ ./lock.sh
lockfile: Sorry, giving up on "./file.lock"
LOCKED

ロックファイルが一定時間よりも古ければ強制的に削除する、というのは、エラーなどでロックが作られっぱなしになるようなケースを踏まえると役に立つかもしれない。

manはこちら

manpages.debian.org

まとめ

他にflock使う方法もある。個人的には、ロックを作成する場合はエラー処理をきちんとやらないと、ロックされっぱなしになると思うのでオプションも含めてlockfileコマンド、うまく使えたら良さげと思った。