Mam polecenie wywołane przez CMD z mojego głównego skryptu powłoki Bourne, które trwa wiecznie.
Chcę zmodyfikować skrypt w następujący sposób:
- Uruchom polecenie CMD równolegle jako proces w tle (
CMD &
). - W głównym skrypcie użyj pętli, aby monitorować pojawiające się polecenie co kilka sekund. Pętla odbija również echo niektórych komunikatów na stdout wskazujących postęp skryptu.
- Wyjdź z pętli po zakończeniu spawnowanego polecenia.
- Przechwyć i zgłoś kod zakończenia zwołanego procesu.
Czy ktoś może mi dać wskazówki, jak to osiągnąć?
Odpowiedzi:
1: W bash
$!
przechowuje PID ostatniego wykonanego procesu w tle. To i tak powie ci, jaki proces monitorować.4:
wait <n>
czeka, aż proces z PID<n>
zostanie zakończony (będzie blokować do zakończenia procesu, więc możesz nie chcieć wywoływać tego, dopóki nie będziesz pewien, że proces się zakończył), a następnie zwraca kod zakończenia ukończonego procesu.2, 3:
ps
lubps | grep " $! "
może powiedzieć, czy proces nadal działa. To od Ciebie zależy, jak zrozumiesz wynik i zdecydujesz, jak blisko jest zakończenia. (ps | grep
nie jest odporny na idiotę. Jeśli masz czas, możesz wymyślić bardziej niezawodny sposób na sprawdzenie, czy proces nadal działa).Oto szkielet skryptu:
źródło
ps -p $my_pid -o pid=
niegrep
jest potrzebne.kill -0 $!
to lepszy sposób sprawdzenia, czy proces nadal działa. W rzeczywistości nie wysyła żadnego sygnału, a jedynie sprawdza, czy proces działa, używając wbudowanej powłoki zamiast procesów zewnętrznych. Jakman 2 kill
powiedziano: „Jeśli sig ma wartość 0, to żaden sygnał nie jest wysyłany, ale sprawdzanie błędów jest nadal wykonywane; można to wykorzystać do sprawdzenia istnienia identyfikatora procesu lub identyfikatora grupy procesów”.kill -0
zwróci wartość niezerową, jeśli nie masz uprawnień do wysyłania sygnałów do uruchomionego procesu. Niestety powraca1
zarówno w tym przypadku, jak iw przypadku, gdy proces nie istnieje. To sprawia, że jest to przydatne, chyba że nie jesteś właścicielem procesu - co może mieć miejsce nawet w przypadku procesów, które utworzyłeś, jeślisudo
zaangażowane jest takie narzędzie, jak i jeśli są ustawione (i prawdopodobnie porzucają uprawnienia).wait
nie zwraca kodu wyjścia w zmiennej$?
. Po prostu zwraca kod zakończenia i$?
jest kodem zakończenia najnowszego programu na pierwszym planie.kill -0
. Oto recenzowane referencje od SO, które pokazują, że komentarz CraigRingera jest uzasadniony:kill -0
zwróci wartość niezerową dla uruchomionych procesów ... aleps -p
zawsze zwróci 0 dla każdego działającego procesu .Tak to rozwiązałem, gdy miałem podobną potrzebę:
źródło
wait
s powodują, że skrypt czeka do samego końca (każdego) procesu.Pid procesu potomnego w tle jest przechowywany w $! . Możesz przechowywać pidy wszystkich procesów potomnych w tablicy, np. PIDS [] .
Zaczekaj, aż proces potomny określony przez każdy identyfikator procesu pid lub specyfikację zadania jobspec zakończy działanie i zwróć kod zakończenia ostatniej oczekiwanej komendy. Jeśli podano specyfikację zadania, oczekiwane są wszystkie procesy w zadaniu. Jeśli nie podano żadnych argumentów, czekane są wszystkie aktualnie aktywne procesy potomne, a zwracany kod wynosi zero. Jeśli podano opcję -n, wait czeka na zakończenie dowolnego zadania i zwraca jego kod zakończenia. Jeśli ani specyfikacja zadania, ani pid nie określają aktywnego procesu potomnego powłoki, zwracany jest kod 127.
Użyj polecenia czekaj , możesz poczekać na zakończenie wszystkich procesów potomnych, w międzyczasie możesz uzyskać status zakończenia każdego procesu potomnego za pomocą $? i zapisz status w STATUS [] . Następnie możesz zrobić coś w zależności od statusu.
Wypróbowałem następujące 2 rozwiązania i działają dobrze. rozwiązanie01 jest bardziej zwięzłe, podczas gdy rozwiązanie02 jest trochę skomplikowane.
rozwiązanie01
rozwiązanie02
źródło
pid=$!; PIDS[$i]=${pid}; ((i+=1))
można napisać prościej, jakoPIDS+=($!)
który po prostu dołącza się do tablicy bez konieczności używania oddzielnej zmiennej do indeksowania lub samego pid. To samo dotyczySTATUS
tablicy.Jak widzę, prawie wszystkie odpowiedzi używają zewnętrznych narzędzi (głównie
ps
) do badania stanu procesu w tle. Istnieje bardziej unixeshowe rozwiązanie, wyłapujące sygnał SIGCHLD. W programie obsługi sygnału należy sprawdzić, który proces potomny został zatrzymany. Można to zrobić poprzezkill -0 <PID>
wbudowane (uniwersalne) lub sprawdzenie istnienia/proc/<PID>
katalogu (specyficzne dla Linuksa) lub użyciejobs
wbudowanego (grzmotnąćkonkretny.jobs -l
zgłasza również pid. W tym przypadku trzecie pole wyjścia może zostać zatrzymane | uruchomione | gotowe | wyjście. ).Oto mój przykład.
Uruchomiony proces nazywa się
loop.sh
. Akceptuje-x
lub liczbę jako argument. For-x
jest zakończone z kodem zakończenia 1. W przypadku liczby czeka num * 5 sekund. Co 5 sekund drukuje swój PID.Proces uruchamiania nazywa się
launch.sh
:Aby uzyskać więcej informacji, zobacz: Uruchomienie procesu ze skryptu bash nie powiodło się
źródło
for i in ${!pids[@]};
using parameter expand_more.źródło
grep -v
. Możesz ograniczyć wyszukiwanie do początku linii:grep '^'$pid
Poza tym możesz to zrobićps p $pid -o pid=
. Ponadtotail -f
nie skończy się, dopóki go nie zabijesz, więc nie sądzę, że jest to dobry sposób na demonstrację tego (przynajmniej bez wskazywania na to). Możesz chcieć przekierować dane wyjściowe swojegops
polecenia do/dev/null
lub zostanie ono wyświetlone na ekranie przy każdej iteracji. Twójexit
powodujewait
pominięcie pliku - prawdopodobnie powinien to być plikbreak
. Ale czywhile
/ps
i nie sąwait
zbędne?kill -0 $pid
? W rzeczywistości nie wysyła żadnego sygnału, a jedynie sprawdza, czy proces działa, używając wbudowanej powłoki zamiast procesów zewnętrznych.bash: kill: (1) - Operation not permitted
Zmieniłbym nieco twoje podejście. Zamiast sprawdzać co kilka sekund, czy polecenie nadal działa i zgłaszać komunikat, skorzystaj z innego procesu, który co kilka sekund zgłasza, że polecenie nadal działa, a następnie zabij ten proces po zakończeniu działania polecenia. Na przykład:
źródło
while kill -0 $pid 2> /dev/null; do X; done
, mam nadzieję, że przyda się komuś w przyszłości, kto przeczyta tę wiadomość;)Nasz zespół miał tę samą potrzebę ze zdalnym skryptem wykonanym przez SSH, który wygasał po 25 minutach bezczynności. Oto rozwiązanie z pętlą monitorowania sprawdzającą proces w tle co sekundę, ale drukującą tylko co 10 minut, aby wyeliminować limit czasu braku aktywności.
źródło
Prosty przykład, podobny do powyższych rozwiązań. Nie wymaga to monitorowania żadnego wyniku procesu. Następny przykład używa taila do śledzenia wyniku.
Użyj tail, aby śledzić wyjście procesu i zakończyć, gdy proces się zakończy.
źródło
Innym rozwiązaniem jest monitorowanie procesów poprzez system plików proc (bezpieczniejsze niż kombi ps / grep); kiedy uruchamiasz proces, ma on odpowiedni folder w / proc / $ pid, więc rozwiązaniem może być
Teraz możesz dowolnie używać zmiennej $ exit_status.
źródło
Syntax error: "else" unexpected (expecting "done")
Dzięki tej metodzie twój skrypt nie musi czekać na proces w tle, będziesz musiał tylko monitorować plik tymczasowy pod kątem statusu wyjścia.
teraz twój skrypt może zrobić cokolwiek innego, podczas gdy ty musisz po prostu monitorować zawartość retFile (może również zawierać dowolne inne informacje, takie jak czas wyjścia).
PS .: przy okazji, kodowałem myślenie w bash
źródło
Może to wykraczać poza twoje pytanie, jednak jeśli martwisz się o czas działania procesów, możesz być zainteresowany sprawdzeniem stanu uruchomionych procesów w tle po pewnym czasie. Dość łatwo jest sprawdzić, które PID-y podrzędne nadal działają
pgrep -P $$
, jednak wymyśliłem następujące rozwiązanie, aby sprawdzić status wyjścia tych PIDów, które już wygasły:które wyjścia:
Uwaga: Możesz zmienić
$pids
na zmienną łańcuchową zamiast tablicy, aby uprościć sprawę, jeśli chcesz.źródło
Moim rozwiązaniem było użycie anonimowego potoku do przekazania statusu do pętli monitorowania. Nie ma plików tymczasowych używanych do wymiany statusu, więc nie ma nic do czyszczenia. Jeśli nie masz pewności co do liczby zadań w tle, może to być stan zerwania
[ -z "$(jobs -p)" ]
.źródło