Preferowane byłoby rozwiązanie niewymagające dodatkowych narzędzi.
ln -s my.pid .lock
przejmie blokadę (a następnie echo $$ > my.pid
), a po awarii może sprawdzić, czy przechowywany PID .lock
jest naprawdę aktywną instancją skryptu
Preferowane byłoby rozwiązanie niewymagające dodatkowych narzędzi.
ln -s my.pid .lock
przejmie blokadę (a następnie echo $$ > my.pid
), a po awarii może sprawdzić, czy przechowywany PID .lock
jest naprawdę aktywną instancją skryptu
Odpowiedzi:
Prawie jak odpowiedź nsg: użyj katalogu blokady . Tworzenie katalogów jest atomowe pod Linuksem, Unixem i * BSD oraz wieloma innymi systemami operacyjnymi.
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
Możesz umieścić PID blokady sh w pliku w katalogu blokady w celu debugowania, ale nie wpadnij w pułapkę myślenia, że możesz sprawdzić ten PID, aby sprawdzić, czy proces blokowania nadal się wykonuje. Na ścieżce leży wiele warunków wyścigowych.
mkdir
nie jest atomowe na NFS (co nie jest w moim przypadku, ale myślę, że należy wspomnieć, że jeśli to prawda)
/tmp
którym zwykle nie jest udostępniany NFS, więc powinno być dobrze.
rm -rf
do usunięcia katalogu blokady. rmdir
zawiedzie, jeśli ktoś (niekoniecznie ty) zdoła dodać plik do katalogu.
Aby dodać do odpowiedzi Bruce'a Edigera i zainspirować się tą odpowiedzią , powinieneś również dodać więcej sprytów do czyszczenia, aby uchronić się przed zakończeniem skryptu:
#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
.
To może być zbyt uproszczone, popraw mnie, jeśli się mylę. Czy to nie jest proste ps
?
#!/bin/bash
me="$(basename "$0")";
running=$(ps h -C "$me" | grep -wv $$ | wc -l);
[[ $running > 1 ]] && exit;
# do stuff below this comment
grep -v $$
. prawdziwe przykłady: stary - 14532, nowy - 1453, stary - 28858, nowy - 858.
grep -v $$
nagrep -v "^${$} "
grep -wv "^$$"
(patrz edycja).
Użyłbym pliku blokady, jak wspomniał Marco
#!/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
nie jest złe?
$$
w pliku blokady. Następnie sleep
przez krótki czas i przeczytaj ponownie. Jeśli PID jest nadal twój, pomyślnie udało ci się zdobyć blokadę. Nie wymaga absolutnie żadnych dodatkowych narzędzi.
Jeśli chcesz się upewnić, że działa tylko jedna instancja skryptu, spójrz na:
Zablokuj skrypt (przed uruchomieniem równoległym)
W przeciwnym razie możesz sprawdzić ps
lub wywołać lsof <full-path-of-your-script>
, ponieważ nie nazwałbym ich dodatkowymi narzędziami.
Suplement :
właściwie pomyślałem o zrobieniu tego w ten sposób:
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
zapewnia to, że działa tylko proces o najniższym poziomie, pid
nawet jeśli wykonujesz kilka wystąpień <your_script>
jednocześnie.
[[(lsof $0 | wc -l) > 2]] && exit
może być wystarczające, czy też jest to podatne na warunki wyścigowe?
Jeszcze jeden sposób, aby upewnić się, że uruchomione jest jedno wystąpienie skryptu bash:
#!/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
pobiera PID istniejącego skryptu, jeśli jest już uruchomiony, lub kończy działanie z kodem błędu 1, jeśli żaden inny skrypt nie jest uruchomiony
Chociaż poprosiłeś o rozwiązanie bez dodatkowych narzędzi, to mój ulubiony sposób, używając flock
:
#!/bin/sh
[ "${FLOCKER}" != "$0" ] && exec env FLOCKER="$0" flock -en "$0" "$0" "$@" || :
echo "servus!"
sleep 10
Pochodzi z sekcji przykładów man flock
, która dalej wyjaśnia:
Jest to przydatny kod podstawowy dla skryptów powłoki. Umieść go na górze skryptu powłoki, który chcesz zablokować, a automatycznie zablokuje się przy pierwszym uruchomieniu. Jeśli env var $ FLOCKER nie jest ustawiony na uruchamiany skrypt powłoki, uruchom flock i weź ekskluzywną nieblokującą blokadę (używając samego skryptu jako pliku blokady) przed ponownym uruchomieniem się z odpowiednimi argumentami. Ustawia również zmienną FLOCKER env var na odpowiednią wartość, aby nie działała ponownie.
Należy wziąć pod uwagę:
flock
, przykładowy skrypt kończy się błędem, jeśli nie można go znaleźćZobacz także /programming/185451/quick-and-dirty-way-to-ensure-only-one-instance-of-a-shell-script-is-running-at .
To jest zmodyfikowana wersja odpowiedzi Anselmo . Chodzi o to, aby utworzyć deskryptor pliku tylko do odczytu przy użyciu samego skryptu bash i użyć flock
do obsługi blokady.
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"
Główną różnicą w stosunku do wszystkich pozostałych odpowiedzi jest to, że ten kod nie modyfikuje systemu plików, używa bardzo małej powierzchni i nie wymaga czyszczenia, ponieważ deskryptor pliku jest zamykany, gdy tylko skrypt zakończy działanie, niezależnie od stanu wyjścia. Dlatego nie ma znaczenia, czy skrypt zawiedzie, czy się powiedzie.
exec 6< "$SCRIPT"
.
Używam cksum, aby sprawdzić, czy mój skrypt naprawdę działa w pojedynczej instancji, nawet jeśli zmieniam nazwę pliku i ścieżkę pliku .
Nie używam pliku pułapki i blokady, ponieważ jeśli mój serwer nagle przestanie działać, muszę ręcznie usunąć plik blokady po uruchomieniu serwera.
Uwaga: Dla grep ps wymagany jest #! / Bin / bash w pierwszym wierszu
#!/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
Lub możesz zakodować cksum wewnątrz skryptu, więc nie musisz się więcej martwić, jeśli chcesz zmienić nazwę pliku, ścieżkę lub treść skryptu .
#!/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
a fsum
jeśli nie mówisz już o sumie kontrolnej.
Możesz użyć tego: https://github.com/sayanarijit/pidlock
sudo pip install -U pidlock
pidlock -n sleepy_script -c 'sleep 10'
Mój kod do ciebie
#!/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}
W oparciu o man flock
ulepszanie tylko:
executing
Tam gdzie umieściłem tutaj sleep 10
, możesz umieścić cały główny skrypt.