Ek araçlar gerektirmeyen bir çözüm tercih edilecektir.
ln -s my.pid .lock
kilidi izler (takip eder echo $$ > my.pid
) ve başarısızlık durumunda .lock
Ek araçlar gerektirmeyen bir çözüm tercih edilecektir.
ln -s my.pid .lock
kilidi izler (takip eder echo $$ > my.pid
) ve başarısızlık durumunda .lock
Yanıtlar:
Neredeyse nsg'nin cevabı gibi: bir kilit dizini kullanın . Dizin oluşturma linux ve unix ve * BSD ve daha birçok işletim sistemi altında atomiktir.
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
Kilitleme sh'in PID'sini hata ayıklama amacıyla kilit dizinindeki bir dosyaya koyabilirsiniz, ancak kilitleme işleminin devam edip etmediğini görmek için PID'yi kontrol edebilirsiniz. Birçok yarış koşulu bu yolda uzanıyor.
mkdir
NFS üzerinde atomik olmayan bir iddia var (ki bu benim için geçerli değil, ama sanırım doğruysa birinden bahsetmeliyim)
/tmp
genellikle NFS'nin paylaşılmadığı bir durumda olacaktı, bu yüzden iyi olmalı.
rm -rf
Kilit dizinini kaldırmak için kullanırdım . rmdir
Birisi (mutlaka sizin için) dizine bir dosya eklemeyi başarırsa başarısız olur.
Bruce Ediger'in cevabına eklemek ve bu cevaptan ilham almak için, senaryo sonlandırmasına karşı korunmak için temizlemeye daha fazla akıllı eklemelisiniz:
#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
if ! mkdir "$LOCKDIR"; then handle failure to lock and exit; fi trap and do processing after if-statement
,.
Bu çok basit olabilir, yanılıyorsam lütfen beni düzeltin. ps
Yeterince basit değil mi?
#!/bin/bash
me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
grep -v $$
. gerçek örnekler: eski - 14532, yeni - 1453, eski - 28858, yeni - 858.
grep -v $$
içingrep -v "^${$} "
grep -wv "^$$"
(bkz. Düzenleme).
Marco tarafından belirtildiği gibi bir kilit dosyası kullanırdım
#!/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
lsof $0
da fena değil?
$$
kilit dosyasını yazarak yarış koşulunu azaltabilirsiniz . Sonra sleep
kısa bir süre için ve tekrar okuyun. PID hala size aitse, kilidi başarıyla aldınız. Kesinlikle ek bir alete ihtiyaç yok.
Komut dosyanızın yalnızca bir örneğinin çalıştığından emin olmak istiyorsanız, şunlara bakın:
Komut dosyanızı kilitleyin (paralel çalışmaya karşı)
Aksi taktirde ps
denetleyebilir veya çağırabilirsiniz lsof <full-path-of-your-script>
, çünkü onlara ek araçlar demem.
Ek :
Aslında böyle yapmayı düşündüm:
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
bu pid
, <your_script>
aynı anda birkaç örneği çatalla ve uygulasanız bile , yalnızca en düşük olan işlemin çalışmaya devam etmesini sağlar .
[[(lsof $0 | wc -l) > 2]] && exit
aslında yeterli olabilir mi, yoksa bu da yarış koşullarına eğilimli mi?
Tek bir bash betiğinin çalıştığından emin olmanın başka bir yolu:
#!/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
Eğer çalışıyorsa mevcut betiğin PID'ini veya başka bir betiğin çalışmaması durumunda hata kodu 1 ile çıkarsa PID'yi alır.
Ek araçlar olmadan bir çözüm istediyseniz de, bu benim en sevdiğim yöntem flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
Bu örnekler bölümünden geliyor. man flock
, daha fazla açıklayan :
Bu kabuk betikleri için yararlı bir boyler kodudur. Kilitlemek istediğiniz kabuk betiğinin en üstüne koyun ve otomatik olarak ilk çalıştırmada kilitlenir. Env var $ FLOCKER çalışmakta olan kabuk betiğine ayarlanmamışsa, sürüyü yürütün ve engelleyici olmayan özel bir kilit alın (betiğin kendisini kilit dosyası olarak kullanarak), kendini doğru argümanlarla yeniden çalıştırmadan önce. Ayrıca FLOCKER env değerini doğru değere ayarlar, böylece tekrar çalışmaz.
Dikkate alınması gereken noktalar:
flock
, örnek komut dosyası bulunamazsa bir hatayla sonlanırAyrıca bkz . Https://stackoverflow.com/questions/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
Bu Anselmo'nun Cevabının değiştirilmiş bir versiyonudur . Fikir, bash betiğini kullanarak salt okunur bir dosya flock
tanıtıcı oluşturmak ve kilidi kullanmak için kullanmaktır .
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"
Diğer tüm cevaplayıcılar arasındaki temel fark, bu kodun dosya sistemini değiştirmemesi, çok düşük bir ayak izi kullanması ve komut dosyası çıkış durumundan bağımsız olarak bittiğinde dosya tanımlayıcısının kapatılması nedeniyle herhangi bir temizlemeye ihtiyaç duymamasıdır. Bu nedenle, komut dosyasının başarısız olup olmaması önemli değildir.
exec 6< "$SCRIPT"
.
Komut dosyamın gerçekten tek örnekle çalıştığını kontrol etmek için cksum kullanıyorum , dosya adı ve dosya yolunu bile değiştirdim .
Bindirme ve kilitleme dosyası kullanmıyorum, çünkü sunucum aniden kapanıyorsa, sunucu açıldıktan sonra el ile kilitlenen dosyayı kaldırmam gerekiyor.
Not: grep ps için ilk satırdaki #! / Bin / bash gereklidir
#!/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
Veya kodunuzun içinde kodlanmış cksum kullanabilirsiniz, bu nedenle komut dosyanızın adını , yolunu veya içeriğini değiştirmek istiyorsanız bir daha endişelenmeyin .
#!/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
mysum
ve fsum
artık bir sağlama toplamından bahsetmediğinizde.
Bunu kullanabilirsiniz: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
Sana kodum
#!/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}
Göre man flock
sadece iyileştirilmesi:
executing
Buraya sleep 10
koyduğum yere, bütün ana senaryoyu koyabilirsiniz.