Jak sprawdzić, czy istnieje identyfikator procesu (PID)

184

W skrypcie bash chcę wykonać następujące czynności (w pseudo-kodzie):

if [ a process exists with $PID ]; then

    kill $PID 

fi

Jakie jest odpowiednie wyrażenie dla instrukcji warunkowej?

Richard H
źródło

Odpowiedzi:

182

Aby sprawdzić istnienie procesu, użyj

kill -0 $pid

Ale tak jak powiedział @unwind, jeśli i tak chcesz go zabić, po prostu

kill $pid

lub będziesz mieć stan wyścigu.

Jeśli chcesz zignorować tekst killi zrobić coś na podstawie kodu wyjścia, możesz to zrobić

if ! kill $pid > /dev/null 2>&1; then
    echo "Could not send SIGTERM to process $pid" >&2
fi
Christoffer Hammarström
źródło
1
patrząc na stronę man, kill -0 to: „kod wyjścia wskazuje, czy sygnał może zostać wysłany”. Czy to faktycznie zabija proces, czy tylko mówi, czy można go zabić?
Richard H
24
killjest nieco źle nazwany, ponieważ niekoniecznie zabija proces. Po prostu wysyła sygnał do procesu. kill $PIDjest równoważne z kill -15 $PIDwysyłaniem sygnału 15 SIGTERM do procesu, który jest instrukcją kończącą. Nie ma sygnału 0, jest to specjalna wartość informująca, killczy po prostu można wysłać sygnał do procesu, co w większości przypadków jest mniej więcej równoznaczne ze sprawdzeniem, czy istnieje. Zobacz linux.die.net/man/2/kill i linux.die.net/man/7/signal
Christoffer Hammarström
43
Ma to problem polegający na tym, że jeśli proces nie jest własnością działającego użytkownika, możesz nie mieć uprawnień do wywołania zabicia -0. Lepiej użyć ps -p $ PID> / dev / null 2> & 1, co pozwala zobaczyć status procesu, nawet jeśli nie masz uprawnień do wysyłania sygnału.
mckoss,
6
@mckoss: W takim razie i tak nie może go zabić.
Christoffer Hammarström
2
Tak więc, sądzę - aby użyć kill -0, w rzeczywistości muszę to zrobić: kill -0 25667 ; echo $?- a następnie, jeśli otrzymam zwrot 0, wówczas proces z tym PID może zostać zabity; a jeśli proces PID (powiedzmy) nie istnieje, $?będzie 1, co oznacza awarię. Czy to jest poprawne?
sdaau
264

Najlepszym sposobem jest:

if ps -p $PID > /dev/null
then
   echo "$PID is running"
   # Do something knowing the pid exists, i.e. the process with $PID is running
fi

Problem z:

kill -0 $PID

oznacza, że ​​kod wyjścia będzie różny od zera, nawet jeśli pid jest uruchomiony i nie masz uprawnień, aby go zabić. Na przykład:

kill -0 1

i

kill -0 $non-running-pid

mają nie dający się odróżnić (niezerowy) kod wyjścia dla zwykłego użytkownika, ale proces inicjowania (PID 1) na pewno działa.

DYSKUSJA

Odpowiedzi omawiające zabójstwo i warunki wyścigu są dokładnie właściwe, jeśli testem jest „zabójstwo”. Przyszedłem szukać ogólnego „ jak testujesz istnienie PID w bash ”.

Metoda / proc jest interesująca, ale w pewnym sensie łamie ducha abstrakcji polecenia „ps”, tzn. Nie musisz szukać w / proc, ponieważ co, jeśli Linus zdecyduje się nazwać plik „exe” czymś innym?

FDS
źródło
1
ps -p zawsze zwraca dla mnie status 0
IttayD
1
ps -p #### działało dobrze dla mnie pod Ubuntu 14.04, +1 dzięki!
Ligemer,
3
ps -p zawsze zwraca kod stanu 0 w os x, ponieważ drukuje pustą listę procesów, gdy nie pasuje do żadnego z uruchomionych procesów
Douglas Correa
Mogę potwierdzić, że w systemie macOS Sierra to działa. Ponadto -pjest to niepotrzebne, przynajmniej w takim przypadku. ps $PIDma dokładnie taki sam wynik.
user137369 16.04.17
Przenośność to dobry powód, aby unikać używania / proc, ale złamanie ABI linuksa nie jest scenariuszem, który szczególnie mnie martwi.
David Roundy,
67
if [ -n "$PID" -a -e /proc/$PID ]; then
    echo "process exists"
fi

lub

if [ -n "$(ps -p $PID -o pid=)" ]

W tej ostatniej formie -o pid=format wyjściowy wyświetla tylko kolumnę ID procesu bez nagłówka. Cudzysłowy są niezbędne, aby niepusty operator łańcuchowy -ndawał poprawny wynik.

użytkownik2683246
źródło
1
Druga metoda działa również na Macu, jako dodatkowy plus (Mac OS X nie ma / proc FS). Możesz jednak uniknąć używania podpowłoki i używać jej zarówno na Macu, jak i Linuksie:if ps -p"$PID" -o "pid=" >/dev/null 2>&1; then echo "Process is running..."; fi
Czy
Niestety psopcje i funkcje różnią się w zależności od platformy, więc wciąż nie jest w pełni przenośny.
tripleee
1
jeśli $PIDjest pusty, to [ -e /proc/$PID ]nadal zwróci true, ponieważ /proc/katalog nadal istnieje.
Magne
34

pspolecenie z -p $PIDmoże to zrobić:

$ ps -p 3531
  PID TTY          TIME CMD
 3531 ?        00:03:07 emacs
oherrala
źródło
11

Masz dwa sposoby:

Zacznijmy od szukania konkretnej aplikacji na moim laptopie:

[root@pinky:~]# ps fax | grep mozilla
 3358 ?        S      0:00  \_ /bin/sh /usr/lib/firefox-3.5/run-mozilla.sh /usr/lib/firefox-3.5/firefox
16198 pts/2    S+     0:00              \_ grep mozilla

Wszystkie przykłady będą teraz szukać PID 3358.

Pierwszy sposób : uruchom „ps aux” i grep dla PID w drugiej kolumnie. W tym przykładzie szukam firefox, a następnie PID:

[root@pinky:~]# ps aux | awk '{print $2 }' | grep 3358
3358

Twój kod będzie więc:

if [ ps aux | awk '{print $2 }' | grep -q $PID 2> /dev/null ]; then
    kill $PID 
fi

Drugi sposób : poszukaj czegoś w /proc/$PIDkatalogu. W tym przykładzie używam „exe”, ale możesz użyć wszystkiego innego.

[root@pinky:~]# ls -l /proc/3358/exe 
lrwxrwxrwx. 1 elcuco elcuco 0 2010-06-15 12:33 /proc/3358/exe -> /bin/bash

Twój kod będzie więc:

if [ -f /proc/$PID/exe ]; then
    kill $PID 
fi

BTW: co jest nie tak kill -9 $PID || true?


EDYTOWAĆ:

Po przemyśleniu tego przez kilka miesięcy .. (około 24 ...) oryginalny pomysł, który tu podałem, to niezły hack, ale wysoce niemożliwy do przeniesienia. Chociaż uczy kilku szczegółów implementacji systemu Linux, nie będzie działać na komputerach Mac, Solaris lub * BSD. Może nawet zawieść w przyszłych jądrach Linuksa. Proszę - użyj „ps” jak opisano w innych odpowiedziach.

elcuco
źródło
przynajmniej część zabicia -9 wydaje się niepoprawna (nie zabija podprocesów)
nurettin
Dlaczego dostaję [: brakujące]] podczas korzystania z pierwszego sposobu?
tenmiles,
1
/proc/$PID/exenie jest zwykłym plikiem. Tak więc [ -f /proc/$PID/exe ]zawsze zwraca falsewynik. Spróbować [ -h /proc/$PID/exe ].
Alexander Yancharuk
8

Wygląda na to, że chcesz

wait $PID

który wróci po $pidzakończeniu.

W przeciwnym razie możesz użyć

ps -p $PID

aby sprawdzić, czy proces jest nadal aktywny (jest to bardziej skuteczne niż kill -0 $pidponieważ będzie działać, nawet jeśli nie jesteś właścicielem pid).

Gagan Gami
źródło
1
czekanie nie jest tak skuteczne, ponieważ proces powinien być potomkiem bieżącej powłoki lub da:pid 123 is not a child of this shell
Calumah
7

Myślę, że to złe rozwiązanie, które otwiera się na warunki wyścigowe. Co się stanie, jeśli proces umrze między testem a wezwaniem do zabicia? Wtedy zabicie się nie powiedzie. Dlaczego więc nie spróbować zabić we wszystkich przypadkach i sprawdzić jego wartość zwracaną, aby dowiedzieć się, jak poszło?

rozwijać
źródło
+1 niestety kod zakończenia kill (1) nie rozróżnia różnych sytuacji błędów (wygląda na to, że zwiększa wartość wyjścia o jeden dla każdego procesu, którego nie zasygnalizował). jeśli OP nie ma nic przeciwko napisaniu własnego opakowania kill (2), może mieć możliwość wyjścia z innymi wartościami w zależności od wartości ERRNO po nieudanym wywołaniu kill (2).
tylko ktoś
w tej chwili właśnie zabijam -9 bez sprawdzenia - pojawia się błąd „proces nie istnieje”, jeśli nie istnieje, co nie jest zbyt uporządkowane. Jak mam sprawdzić, co się stało?
Richard H
14
Nie niedbale kill -9. To natychmiast zabija proces, nie dając mu szansy na posprzątanie po sobie. Zamiast tego użyj killodpowiednika kill -15. Jeśli to nie zadziała, powinieneś dowiedzieć się, dlaczego i tylko w ostateczności kill -9.
Christoffer Hammarström
0

tutaj przechowuję PID w pliku o nazwie .pid (który jest podobny do / run / ...) i wykonuję skrypt tylko wtedy, gdy jeszcze nie jest wykonywany.

#!/bin/bash
if [ -f .pid ]; then
  read pid < .pid
  echo $pid
  ps -p $pid > /dev/null
  r=$?
  if [ $r -eq 0 ]; then
    echo "$pid is currently running, not executing $0 twice, exiting now..."
    exit 1
  fi
fi

echo $$ > .pid

# do things here

rm .pid

Uwaga: istnieje warunek wyścigu, ponieważ nie sprawdza on, jak nazywany jest ten pid. jeśli system zostanie zrestartowany, a plik pid istnieje, ale jest używany przez inną aplikację, może to prowadzić do „nieprzewidzianych konsekwencji”.

invalidmagic
źródło
0

Na przykład w GNU / Linux możesz użyć:

Pid=$(pidof `process_name`)

if [ $Pid > 0 ]; then

   do something
else

   do something
fi 

Lub coś w tym rodzaju

Pin=$(ps -A | grep name | awk 'print $4}')
echo $PIN

i to pokazuje nazwę aplikacji, tylko nazwę bez identyfikatora.

inukaze
źródło
1
pidofnie zwraca liczby ujemnej, ponieważ ujemny PID nie ma sensu i nie możesz zabić init, więc twoje warunkowe nie ma sensu (a poza tym musisz uciec przed, >aby uniemożliwić przekierowanie). Chcesz sprawdzić pusty wynik, ale oczywiście, jak każde przyzwoite narzędzie, pidofustawia kod wyjścia informujący, czy zadziałał, więc właściwym rozwiązaniem jest if Pid=$(pidof 'process_name'); then ...lub (jeśli Pidpóźniej nie będziesz potrzebować tej wartości ) po prostuif pidof 'process_name'; then...
tripleee
@tripleee ma rację, pidofprzykład jest pełen nieporozumień na temat tego, jak testdziała bash . gnu.org/software/bash/manual/html_node/…
Bruno Bronosky
0

poniższy kod sprawdza, czy mój proces jest uruchomiony, jeśli tak, nic nie rób.

sprawdzajmy nowe wiadomości z Amazon SQS co godzinę i tylko wtedy, gdy proces nie jest uruchomiony.

#!/bin/bash
PID=$(ps aux | grep '/usr/bin/python2.7 manage.py SES__boto3_sqs_read' | grep -v grep | awk '{print $2}')
if [[ -z $PID ]]; then
    /usr/bin/python2.7 /home/brian/djcode/proyectoONE/manage.py SES__boto3_sqs_read
else
    echo "do nothing, just smile =)"
fi
exit $?
Brian Sanchez
źródło