Dans un script bash, je souhaite effectuer les opérations suivantes (en pseudo-code):
if [ a process exists with $PID ]; then
kill $PID
fi
Quelle est l'expression appropriée pour l'instruction conditionnelle?
Pour vérifier l’existence d’un processus, utilisez
kill -0 $pid
Mais comme @unwind a dit, si vous voulez le tuer de toute façon, juste
kill $pid
ou vous aurez une condition de concurrence.
Si vous voulez ignorer la sortie texte de kill
et faire quelque chose en fonction du code de sortie, vous pouvez
if ! kill $pid > /dev/null 2>&1; then
echo "Could not send SIGTERM to process $pid" >&2
fi
Le meilleur moyen est:
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
Le problème avec:
kill -0 $PID
est le code de sortie sera non nul même si le pid est en cours d'exécution et vous n'avez pas la permission de le tuer. Par exemple:
kill -0 1
et
kill -0 $non-running-pid
avoir un code de sortie indiscernable (non nul) pour un utilisateur normal, mais le processus init (PID 1) est certainement en cours d'exécution.
Les réponses sur les conditions de mise à mort et de race sont exactes si le corps du test est une "mise à mort". Je suis venu à la recherche du général " comment testez-vous l'existence d'un PID dans bash ".
La méthode/proc est intéressante, mais dans un sens, elle brise l’esprit de l’abstraction de la commande "ps", c’est-à-dire que vous n’avez pas besoin d’aller dans/proc car que se passera-t-il si Linus décide d’appeler autre chose le fichier "exe"?
if [ -n "$PID" -a -e /proc/$PID ]; then
echo "process exists"
fi
ou
if [ -n "$(ps -p $PID -o pid=)" ]
Dans la dernière forme, -o pid=
est un format de sortie permettant d'afficher uniquement la colonne d'ID de processus sans en-tête. Les guillemets sont nécessaires pour que l'opérateur de chaîne non vide -n
donne un résultat valide.
La commande ps
avec -p $PID
peut faire ceci:
$ ps -p 3531
PID TTY TIME CMD
3531 ? 00:03:07 emacs
Vous avez deux moyens:
Commençons par rechercher une application spécifique dans mon ordinateur portable:
[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
Tous les exemples maintenant rechercheront le PID 3358.
Première manière : Exécutez "ps aux" et grep pour le PID dans la deuxième colonne. Dans cet exemple, je cherche firefox, puis son PID:
[root@pinky:~]# ps aux | awk '{print $2 }' | grep 3358
3358
Donc, votre code sera:
if [ ps aux | awk '{print $2 }' | grep -q $PID 2> /dev/null ]; then
kill $PID
fi
Deuxième manière : Il suffit de chercher quelque chose dans le répertoire /proc/$PID
. J'utilise "exe" dans cet exemple, mais vous pouvez utiliser n'importe quoi d'autre.
[root@pinky:~]# ls -l /proc/3358/exe
lrwxrwxrwx. 1 elcuco elcuco 0 2010-06-15 12:33 /proc/3358/exe -> /bin/bash
Donc, votre code sera:
if [ -f /proc/$PID/exe ]; then
kill $PID
fi
BTW: qu'est-ce qui ne va pas avec kill -9 $PID || true
?
MODIFIER:
Après y avoir réfléchi pendant quelques mois ... (environ 24 ...), l’idée originale que j’ai donnée ici est un joli bidouillage, mais très peu transportable. Bien qu'il enseigne quelques détails d'implémentation de Linux, il ne fonctionnera pas sur Mac, Solaris ou * BSD. Cela pourrait même échouer sur les futurs noyaux Linux. Merci d'utiliser "ps" comme décrit dans d'autres réponses.
Je pense que c'est une mauvaise solution, qui ouvre la voie à des conditions de course. Que se passe-t-il si le processus meurt entre votre test et votre appel à tuer? Alors tuer va échouer. Alors pourquoi ne pas simplement essayer de tuer dans tous les cas et vérifier sa valeur de retour pour savoir comment cela s'est passé?
On dirait que tu veux
wait $PID
qui retournera quand $pid
finira.
Sinon, vous pouvez utiliser
ps -p $PID
vérifier si le processus est toujours actif (c'est plus efficace que kill -0 $pid
car cela fonctionnera même si vous ne possédez pas le pid).
Par exemple, sous GNU/Linux, vous pouvez utiliser:
Pid=$(pidof `process_name`)
if [ $Pid > 0 ]; then
do something
else
do something
fi
Ou quelque chose comme
Pin=$(ps -A | grep name | awk 'print $4}')
echo $PIN
et cela vous montre le nom de l'application, juste le nom sans ID.
le code ci-dessous vérifie si mon processus est en cours d'exécution, si rien ne se passe.
vérifions les nouveaux messages d'Amazon SQS toutes les heures et uniquement si le processus n'est pas en cours d'exécution.
#!/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 $?
ici, je stocke le PID dans un fichier appelé .pid (qui est un peu comme/run/...) et n'exécute le script que s'il n'est pas déjà exécuté.
#!/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
note: il y a une condition de concurrence, car il ne vérifie pas comment ce pid est appelé. Si le système est redémarré et que le fichier .pid existe mais qu'il est utilisé par une application différente, cela peut entraîner des "conséquences imprévues".