追加のツールを必要としないソリューションが推奨されます。
Nsgの答えとほぼ同じです。ロックを使用しますdirectory。ディレクトリの作成は、linuxとunix、* BSD、および他の多くのOSでアトミックです。
if mkdir $LOCKDIR
then
# Do important, exclusive stuff
if rmdir $LOCKDIR
then
echo "Victory is mine"
else
echo "Could not remove lock dir" >&2
fi
else
# Handle error condition
...
fi
デバッグのために、ロックshのPIDをロックディレクトリ内のファイルに入れることができますが、そのPIDをチェックしてロックプロセスがまだ実行されているかどうかを確認できるという考えの罠に陥らないでください。多くの競合状態がその道を横切っています。
Bruce Edigerの回答 に追加し、 この回答 に触発された場合は、スクリプトの終了を防ぐためにクリーンアップにスマートを追加する必要があります。
#Remove the lock directory
function cleanup {
if rmdir $LOCKDIR; then
echo "Finished"
else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1
fi
}
if mkdir $LOCKDIR; then
#Ensure that if we "grabbed a lock", we release it
#Works for SIGTERM and SIGINT(Ctrl-C)
trap "cleanup" EXIT
echo "Acquired lock, running"
# Processing starts here
else
echo "Could not create lock directory '$LOCKDIR'"
exit 1
fi
これは単純すぎる可能性があります。間違っている場合は修正してください。単純なps
では不十分ですか?
#!/bin/bash
me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
Bashスクリプトの単一のインスタンスが実行されることを確認するもう1つの方法:
#!/bin/bash
# Check if another instance of script is running
pidof -o %PPID -x $0 >/dev/null && echo "ERROR: Script $0 already running" && exit 1
...
pidof -o %PPID -x $0
は、既存のスクリプトが既に実行されている場合はそのPIDを取得し、他のスクリプトが実行されていない場合はエラーコード1で終了します。
追加のツールなしで解決策を求めてきましたが、これがflock
を使用する私のお気に入りの方法です。
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
これはman flock
の例のセクションから来ており、さらに説明しています:
これは、シェルスクリプトの有用なボイラープレートコードです。ロックするシェルスクリプトの先頭に配置すると、最初の実行時に自動的にロックされます。 env var $ FLOCKERが実行中のシェルスクリプトに設定されていない場合は、flockを実行し、(スクリプト自体をロックファイルとして使用して)排他的な非ブロックロックを取得してから、正しい引数で自身を再実行します。また、FLOCKER env varを正しい値に設定して、再度実行されないようにします。
考慮すべき点:
flock
が必要です。サンプルスクリプトが見つからない場合、エラーで終了します。マルコが言ったように、私はロックファイルを使います
#!/bin/bash
# Exit if /tmp/lock.file exists
[ -f /tmp/lock.file ] && exit
# Create lock file, sleep 1 sec and verify lock
echo $$ > /tmp/lock.file
sleep 1
[ "x$(cat /tmp/lock.file)" == "x"$$ ] || exit
# Do stuff
sleep 60
# Remove lock file
rm /tmp/lock.file
スクリプトのインスタンスが1つだけ実行されていることを確認したい場合は、以下をご覧ください。
それ以外の場合は、ps
を確認するか、lsof <full-path-of-your-script>
を呼び出すことができます。追加のツールを呼び出さないためです。
補足:
実際に私はこのようにすることを考えました:
for LINE in `lsof -c <your_script> -F p`; do
if [ $$ -gt ${LINE#?} ] ; then
echo "'$0' is already running" 1>&2
exit 1;
fi
done
これにより、<your_script>
の複数のインスタンスを同時にフォークして実行した場合でも、pid
が最も低いプロセスのみが実行を継続することが保証されます。
私はcksumを使用して、スクリプトが本当に1つのインスタンスを実行していることを確認しています。ファイル名とファイルパスを変更してもです。
トラップ&ロックファイルを使用していません。サーバーが突然ダウンした場合、サーバーが起動した後に手動でロックファイルを削除する必要があるためです。
注:最初の行の#!/ bin/bashはgrep psに必要です
#!/bin/bash
checkinstance(){
nprog=0
mysum=$(cksum $0|awk '{print $1}')
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(cksum /proc/$i/cwd/$cmd|awk '{print $1}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
または、スクリプト内でcksumをハードコーディングできるので、スクリプトのファイル名、パス、または内容を変更する場合でも、心配する必要はありません。
#!/bin/bash
mysum=1174212411
checkinstance(){
nprog=0
for i in `ps -ef |grep /bin/bash|awk '{print $2}'`;do
proc=$(ls -lha /proc/$i/exe 2> /dev/null|grep bash)
if [[ $? -eq 0 ]];then
cmd=$(strings /proc/$i/cmdline|grep -v bash)
if [[ $? -eq 0 ]];then
fsum=$(grep mysum /proc/$i/cwd/$cmd|head -1|awk -F= '{print $2}')
if [[ $mysum -eq $fsum ]];then
nprog=$(($nprog+1))
fi
fi
fi
done
if [[ $nprog -gt 1 ]];then
echo $0 is already running.
exit
fi
}
checkinstance
#--- run your script bellow
echo pass
while true;do sleep 1000;done
これは Anselmo's Answer の修正版です。アイデアは、bashスクリプト自体を使用して読み取り専用ファイル記述子を作成し、flock
を使用してロックを処理することです。
SCRIPT=`realpath $0` # get absolute path to the script itself
exec 6< "$SCRIPT" # open bash script using file descriptor 6
flock -n 6 || { echo "ERROR: script is already running" && exit 1; } # lock file descriptor 6 OR show error message if script is already running
echo "Run your single instance code here"
他のすべての回答との主な違いは、このコードはファイルシステムを変更せず、フットプリントが非常に低く、スクリプトが終了状態に関係なく終了するとすぐにファイル記述子が閉じられるため、クリーンアップを必要としないことです。したがって、スクリプトが失敗したか成功したかは関係ありません。
これを使用できます: https://github.com/sayanarijit/pidlock
Sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
あなたへの私のコード
#!/bin/bash
script_file="$(/bin/readlink -f $0)"
lock_file=${script_file////_}
function executing {
echo "'${script_file}' already executing"
exit 1
}
(
flock -n 9 || executing
sleep 10
) 9> /var/lock/${lock_file}
に基づく man flock
、改善のみ:
executing
ここにsleep 10
、すべてのメインスクリプトを配置できます。
この便利なパッケージは、あなたが探しているものを実行します。
https://github.com/krezreb/singleton
インストールしたら、コマンドの前にsingleton LOCKNAME
を付けます。
例えばsingleton LOCKNAME PROGRAM ARGS...