前書き :同じタイミングで動かれると困る
同じシェルスクリプトが二重起動すると、処理によっては「無駄で時間のかかる処理を二重に行い、システムリソースを消費する事」があります。
例えば、cron/rsyncコマンドを組み合わせてローカルファイル(音楽、動画など)をリモートサーバへ定期バックアップしている場合、一つ前の定期バックアップが終わる前に、次の定期バックアップが開始される可能性があります。タイミング次第では、同じファイルのバックアップを試み、ネットワークやI/Oリソースを無駄に消費してしまいます。
そこで、本記事ではシェルスクリプトの起動時に、同じスクリプトが実行中かどうかを確認する方法を紹介します。
プロセスIDとスクリプト名を用いて二重起動を防止
二重起動を防止するには、以下の処理を行います。
- シェルスクリプトのプロセスIDを取得
- 現在実行中のプロセス一覧に対して、同名のシェルスクリプトが存在するかをpgrepコマンドで検索
実装例は、以下の通りです。
function IsRunning() {
if [ $$ -ne $(pgrep -fo "$0") ]; then
echo "起動済みです。"
exit 1
fi
}
プロセスIDはシェルの特殊変数$$で取得でき、シェルスクリプト名も特殊変数$0で取得できます。
pgrepコマンドはプロセス一覧を検索し、検索にヒットしたプロセスIDを返します。上記の実装例で使用しているオプションの意味は、
- fオプション:検索対象をフルPATHのプロセス名に変更
- oオプション:検索にヒットしたプロセスの中から最も古いプロセスIDのみを返すように変更
です。
fオプションは検索マッチ数を増やすため(シェルスクリプト名を確実に検索ヒットさせるため)に付与し、oオプションはpgrepコマンド結果をプロセスID1個分とするために付与しています。
pgrepコマンドが複数のプロセスIDを返した場合は以下のような結果となるため、シェルスクリプトの二重起動を検出できていても"if [ $$ -ne $(pgrep -fo “$0”) ];“部分の判定が正しく動きません。
$ pgrep -f bash 11153
13960
16191
実行例(検証)
検証用シェルスクリプトとして、only.shスクリプトを用意します。only.shスクリプトは、同名シェルスクリプトが実行済みかをチェックした後に無限ループを行います。
#!/bin/bash
function IsRunning() {
if [ $$ -ne $(pgrep -fo "$0") ]; then
echo "起動済みです。"
exit 1
fi
}
# 同名シェルスクリプトの実行チェック
IsRunning
# 無限ループ
while true
do
:
done
検証手順として、
- only.shスクリプトをバックグラウンド実行(&を付けて実行)
- only.shスクリプトをファオアグラウンド実行
を順に行います。
バックグラウンド実行中のonly.shスクリプトは無限ループ中なので、killしない限り実行を継続しています。その状態でonly.shスクリプトを再実行すれば、同名シェルスクリプトの二重起動を検出できます。
以下、実行例です。
(注釈):バックグラウンド実行
$ ./only.sh &
[1] 16065
(注釈):フォアグラウンド実行
$ ./only.sh
起動済みです。 (注釈) 二重起動が防止できている。
(注釈かつ補足):pgrepコマンドは、以下のような結果を返している。
$ pgrep -fo "only.sh"
16065
二回目のonly.shスクリプトの実行(フォアグラウンド実行)において、二重起動防止ができている事が確認できました。
