ksaitoの日記

日々試したことの覚え書き

シェルの重複起動防止

同じシェルを複数起動しないようにするには、ロックファイルを使う方法が良く知られています。
単純明快な方法ですがシェルがkill -9で停止した場合には、タイミングによってロックファイルが削除されないことがあります。
メンテナンスフリーで重複起動を防止するには、すこしトリッキーな方法を使う必要があります。
どちらが良いというより状況により使い分けが必要となります。

ロックファイルを使った方法

ロックファイルは、シグナルで停止した場合にtrapを使ってロックファイルを削除できます。
しかし、KILLシグナルを受信した場合には、trapは機能しません。
結果的にロックファイルが削除されず、手動で削除しない限り起動しなくなります。
こうしたことは、レアケースなのでロックファイルの仕組みを理解して手動で対応することができれば、よく知られている方法で十分に良い実装と言えます。

#! /bin/sh
# ロックファイルを使ったシェルの二重起動防止
# この処理では、kill -9で終了された場合にロックファイルを手動で削除する必要があります。

# ロックファイル名とシグナル受信時の後処理を設定
LOCKFILE=`basename $0`.lock
trap 'echo "trapped."; rm -f ${LOCKFILE}; exit 1' 1 2 3 15

# ロックファイルがあれば起動しない
if [ -e ${LOCKFILE} ]
then
    exit 1;
fi

# ロックファイルがなければ、ロックファイルを作成してメイン処理を実行
touch ${LOCKFILE}
echo main process running
sleep $1
echo main process done.

# ロックファイルを削除して終了
rm $LOCKFILE
exit 0

ロックファイルを使わない方法

pgrepを使って同じ名前のプロセスで一番古いプロセスIDを探して、自分のプロセス番号と比較することで重複起動を防止することができます。
この方法だとKILLシグナルでロックファイルが残るといったことはないです。
ロックファイルと違ってリカバリが不要になりますが、広く知られている方法ではなくロックファイルのように単純明快ではないので、あとで見たときに何の処理か分らなくなるかもしれません。

#! /bin/sh
# ロックファイルを使ったシェルの二重起動防止
# この処理では、起動しているプロセスから判断するのでkill -9でプロセスが終了してもロックファイルの削除などの処理は必要ありません。

# 同じ名前のプロセスが起動していたら起動しない。
if [ "" != "`pgrep -fo $0`" ]
then
    exit 1;
fi

# 同じ名前のプロセスが起動していなかったらメイン処理を実行
echo main process running
sleep $1
echo main process done.

引数も含めて重複を確認するには、以下のようにします。

$ cat test2.sh 
#! /bin/sh
# ロックファイルを使ったシェルの二重起動防止
# この処理では、起動しているプロセスから判断するのでkill -9でプロセスが終了してもロックファイルの削除などの処理は必要ありません。

# 同じ名前のプロセスが起動していたら起動しない。
COMM="$0 $*"
if [ "" != "`pgrep -fo $COMM`" ]
then
  exit 1;
fi

# 同じ名前のプロセスが起動していなかったらメイン処理を実行
echo main process running
sleep $1
echo main process done.