@ jw013 dzięki! Więc może coś takiego ln -s my.pid .lockprzejmie blokadę (a następnie echo $$ > my.pid), a po awarii może sprawdzić, czy przechowywany PID .lockjest naprawdę aktywną instancją skryptu
Tobias Kienzler
Odpowiedzi:
18
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 stuffif rmdir $LOCKDIR
then
echo "Victory is mine"else
echo "Could not remove lock dir">&2fielse# 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.
Rozważę użycie zapisanego PID, aby sprawdzić, czy instancja blokująca nadal żyje. Jednakże, tutaj jest twierdzenie, które mkdirnie jest atomowe na NFS (co nie jest w moim przypadku, ale myślę, że należy wspomnieć, że jeśli to prawda)
Tobias Kienzler
Tak, z całą pewnością używaj zapisanego PID, aby sprawdzić, czy proces blokowania jest nadal wykonywany, ale nie próbuj robić nic innego niż rejestrowanie wiadomości. Praca polegająca na sprawdzeniu zapisanego pid, utworzeniu nowego pliku PID itp. Pozostawia duże okno dla wyścigów.
Bruce Ediger,
Ok, jak stwierdził Ihunath , najprawdopodobniej byłby nim lockdir, w /tmpktórym zwykle nie jest udostępniany NFS, więc powinno być dobrze.
Tobias Kienzler,
Chciałbym użyć rm -rfdo usunięcia katalogu blokady. rmdirzawiedzie, jeśli ktoś (niekoniecznie ty) zdoła dodać plik do katalogu.
chepner
18
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 directoryfunction cleanup {if rmdir $LOCKDIR;then
echo "Finished"else
echo "Failed to remove lock directory '$LOCKDIR'"
exit 1fi}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 hereelse
echo "Could not create lock directory '$LOCKDIR'"
exit 1fi
Korzystałem z tego warunku przez tydzień i 2 razy nie przeszkadzało to w rozpoczęciu nowego procesu. Zrozumiałem, na czym polega problem - nowy pid jest podciągiem starego i zostaje ukryty grep -v $$. prawdziwe przykłady: stary - 14532, nowy - 1453, stary - 28858, nowy - 858.
Naktibalda
Naprawiłem to, zmieniając grep -v $$nagrep -v "^${$} "
Naktibalda
@Naktibalda dobry połów, dzięki! Możesz to również naprawić za pomocą grep -wv "^$$"(patrz edycja).
terdon
Dzięki za tę aktualizację. Mój wzór czasami nie udawał się, ponieważ krótsze piddy były wypełnione spacjami.
(Myślę, że zapomniałeś utworzyć plik blokady) Co z warunkami wyścigu ?
Tobias Kienzler,
ops :) Tak, warunki wyścigu są problemem w moim przykładzie, zwykle piszę godzinowe lub codzienne zadania crona, a warunki wyścigu są rzadkie.
nsg
Nie powinny one również mieć znaczenia w moim przypadku, ale należy o tym pamiętać. Może używanie też lsof $0nie jest złe?
Tobias Kienzler,
Możesz zmniejszyć stan wyścigu, pisząc $$w pliku blokady. Następnie sleepprzez 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.
manatwork
1
Nigdy nie używałem lsof do tego celu, to powinno działać. Zauważ, że lsof jest naprawdę wolny w moim systemie (1-2 sekundy) i najprawdopodobniej jest dużo czasu na warunki wyścigowe.
nsg
3
Jeśli chcesz się upewnić, że działa tylko jedna instancja skryptu, spójrz na:
Dziękuję za link, ale czy mógłbyś podać istotne części w swojej odpowiedzi? Powszechną polityką w SE jest zapobieganie gniciu linków ... Ale coś takiego [[(lsof $0 | wc -l) > 2]] && exitmoże być wystarczające, czy też jest to podatne na warunki wyścigowe?
Tobias Kienzler,
Masz rację, zasadniczej części mojej odpowiedzi brakowało, a jedynie zamieszczanie linków jest dość kiepskie. Dodałem własną sugestię do odpowiedzi.
user1146332,
3
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
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ę:
Wymaga flock , przykładowy skrypt kończy się błędem, jeśli nie można go znaleźć
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ć flockdo 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.
Powinieneś zawsze cytować wszystkie odwołania do zmiennych powłoki, chyba że masz dobry powód, aby tego nie robić i jesteś pewien, że wiesz, co robisz. Więc powinieneś to robić exec 6< "$SCRIPT".
Scott
@Scott Zmieniłem kod zgodnie z Twoimi sugestiami. Wielkie dzięki.
John Doe,
1
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
Proszę dokładnie wyjaśnić, w jaki sposób zakodowanie sumy kontrolnej jest dobrym pomysłem.
Scott
nie suma kontrolna na stałe, to jedyny klucz tożsamości do skryptu, gdy uruchomiona zostanie inna instancja, sprawdzi inny proces skryptu powłoki i najpierw przechwyci plik, jeśli klucz tożsamości znajduje się w tym pliku, więc oznacza to, że instancja już działa.
arputra
DOBRZE; proszę edytować swoje odpowiedzi wyjaśnić. W przyszłości nie publikuj wielu 30-liniowych bloków kodu, które wyglądają, jakby były (prawie) identyczne, bez mówienia i wyjaśniania, czym się różnią. I nie mów takich rzeczy, jak „możesz zakodować [sic] cksum w swoim skrypcie” i nie kontynuuj używania nazw zmiennych, mysuma fsumjeśli nie mówisz już o sumie kontrolnej.
Scott
Wygląda interesująco, dzięki! Witamy w unix.stackexchange :)
ln -s my.pid .lock
przejmie blokadę (a następnieecho $$ > my.pid
), a po awarii może sprawdzić, czy przechowywany PID.lock
jest naprawdę aktywną instancją skryptuOdpowiedzi:
Prawie jak odpowiedź nsg: użyj katalogu blokady . Tworzenie katalogów jest atomowe pod Linuksem, Unixem i * BSD oraz wieloma innymi systemami operacyjnymi.
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.
źródło
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:
źródło
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
?źródło
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
źródło
lsof $0
nie jest złe?$$
w pliku blokady. Następniesleep
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:
zapewnia to, że działa tylko proces o najniższym poziomie,
pid
nawet jeśli wykonujesz kilka wystąpień<your_script>
jednocześnie.źródło
[[(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:
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źródło
Chociaż poprosiłeś o rozwiązanie bez dodatkowych narzędzi, to mój ulubiony sposób, używając
flock
:Pochodzi z sekcji przykładów
man flock
, która dalej wyjaśnia: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 .
źródło
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.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.
źródło
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
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 .
źródło
mysum
afsum
jeśli nie mówisz już o sumie kontrolnej.Możesz użyć tego: https://github.com/sayanarijit/pidlock
źródło
Mój kod do ciebie
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.źródło