157 votes

Obtenir le code de sortie d'un processus d'arrière-plan

J'ai une commande CMD appelée depuis mon shell bourne principal script qui prend une éternité.

Je veux modifier le script comme suit :

  1. Exécuter la commande CMD en parallèle en tant que processus d'arrière-plan ( CMD & ).
  2. Dans le script principal, ayez une boucle pour surveiller la commande spawnée toutes les quelques secondes. La boucle renvoie également quelques messages à stdout indiquant la progression du script.
  3. Quitte la boucle lorsque la commande engendrée se termine.
  4. Capture et rapporte le code de sortie du processus engendré.

Quelqu'un peut-il me donner des indications pour y parvenir ?

2 votes

...et le gagnant est ?

4 votes

@TrueY bob ne s'est pas connecté depuis le jour où il a posé la question. Il est peu probable que nous le sachions jamais !

153voto

mob Points 61524

1 : Dans bash, $! contient le PID du dernier processus d'arrière-plan qui a été exécuté. Cela vous indiquera le processus à surveiller, de toute façon.

4 : wait <n> attend que le processus avec PID <n> est terminé (il bloquera jusqu'à ce que le processus soit terminé, donc vous ne voudrez peut-être pas l'appeler avant d'être sûr que le processus est terminé), et renvoie ensuite le code de sortie du processus terminé.

2, 3 : ps o ps | grep " $! " peut vous dire si le processus est toujours en cours. C'est à vous de comprendre la sortie et de décider s'il est sur le point de se terminer. ( ps | grep n'est pas à toute épreuve. Si vous avez le temps, vous pouvez trouver un moyen plus robuste de savoir si le processus est toujours en cours d'exécution).

Voici un squelette de script :

# simulate a long process that will have an identifiable exit code
(sleep 15 ; /bin/false) &
my_pid=$!

while   ps | grep " $my_pid "     # might also need  | grep -v grep  here
do
    echo $my_pid is still in the ps output. Must still be running.
    sleep 3
done

echo Oh, it looks like the process is done.
wait $my_pid
# The variable $? always holds the exit code of the last command to finish.
# Here it holds the exit code of $my_pid, since wait exits with that code. 
my_status=$?
echo The exit status of the process was $my_status

17 votes

ps -p $my_pid -o pid= ni grep est nécessaire.

1 votes

@Dennis Williamson ps a de nombreuses saveurs. Votre appel ne fonctionne pas pour moi mais ps -p$my_pid fait. Votre point plus large que grep n'est pas nécessaire est correct.

0 votes

Hmmm en fait je n'arrive pas à trouver un bon moyen d'éviter grep sur Cygwin. ps -p$pid a toujours un statut de sortie de 0, que $pid existe ou non. Je pourrais dire quelque chose comme while [ 'ps -p$pid | wc -l' \> 1 ] mais c'est loin d'être une amélioration...

9voto

Abu Aqil Points 373
#/bin/bash

#pgm to monitor
tail -f /var/log/messages >> /tmp/log&
# background cmd pid
pid=$!
# loop to monitor running background cmd
while :
do
    ps ax | grep $pid | grep -v grep
    ret=$?
    if test "$ret" != "0"
    then
        echo "Monitored pid ended"
        break
    fi
    sleep 5

done

wait $pid
echo $?

2 votes

Voici une astuce pour éviter le grep -v . Vous pouvez limiter la recherche au début de la ligne : grep '^'$pid De plus, vous pouvez faire ps p $pid -o pid= de toute façon. Aussi, tail -f ne s'arrêtera pas tant que vous ne l'aurez pas tué, donc je ne pense pas que ce soit une très bonne façon de faire une démonstration (du moins sans le signaler). Vous pourriez vouloir rediriger la sortie de votre script ps pour /dev/null ou il ira à l'écran à chaque itération. Votre exit provoque le wait à sauter - il devrait probablement s'agir d'une break . Mais est-ce que les while / ps et le wait redondant ?

5 votes

Pourquoi tout le monde oublie kill -0 $pid ? Il n'envoie pas réellement de signal, il vérifie seulement que le processus est vivant, en utilisant un shell intégré au lieu de processus externes.

3 votes

Parce que vous pouvez seulement tuer un processus que vous possédez : bash: kill: (1) - Operation not permitted

5voto

William Pursell Points 56211

Je modifierais légèrement votre approche. Plutôt que de vérifier toutes les quelques secondes si la commande est toujours en cours d'exécution et de rapporter un message, mettez en place un autre processus qui rapporte toutes les quelques secondes que la commande est toujours en cours d'exécution, puis tuez ce processus lorsque la commande se termine. Par exemple :

#!/bin/sh

cmd() { sleep 5; exit 24; }

cmd &   # Run the long running process
pid=$!  # Record the pid

# Spawn a process that coninually reports that the command is still running
while echo "$(date): $pid is still running"; do sleep 1; done &
echoer=$!

# Set a trap to kill the reporter when the process finishes
trap 'kill $echoer' 0

# Wait for the process to finish
if wait $pid; then
    echo "cmd succeeded"
else
    echo "cmd FAILED!! (returned $?)"
fi

0 votes

Superbe modèle, merci de le partager ! Je crois qu'au lieu du piège, on peut aussi faire while kill -0 $pid 2> /dev/null; do X; done J'espère que cela sera utile à quelqu'un d'autre qui lira ce message à l'avenir ;)

2voto

Darren Weber Points 151

Un exemple simple, similaire aux solutions ci-dessus. Cela ne nécessite pas de surveiller la sortie d'un processus. L'exemple suivant utilise tail pour suivre la sortie.

$ echo '#!/bin/bash' > tmp.sh
$ echo 'sleep 30; exit 5' >> tmp.sh
$ chmod +x tmp.sh
$ ./tmp.sh &
[1] 7454
$ pid=$!
$ wait $pid
[1]+  Exit 5                  ./tmp.sh
$ echo $?
5

Utilisez tail pour suivre la sortie du processus et quitter lorsque le processus est terminé.

$ echo '#!/bin/bash' > tmp.sh
$ echo 'i=0; while let "$i < 10"; do sleep 5; echo "$i"; let i=$i+1; done; exit 5;' >> tmp.sh
$ chmod +x tmp.sh
$ ./tmp.sh
0
1
2
^C
$ ./tmp.sh > /tmp/tmp.log 2>&1 &
[1] 7673
$ pid=$!
$ tail -f --pid $pid /tmp/tmp.log
0
1
2
3
4
5
6
7
8
9
[1]+  Exit 5                  ./tmp.sh > /tmp/tmp.log 2>&1
$ wait $pid
$ echo $?
5

1voto

Une autre solution consiste à surveiller les processus via le système de fichiers proc (plus sûr que la combinaison ps/grep) ; lorsque vous démarrez un processus, il a un dossier correspondant dans /proc/$pid, la solution pourrait donc être la suivante

#!/bin/bash
....
doSomething &
local pid=$!
while [ -d /proc/$pid ]; do # While directory exists, the process is running
    doSomethingElse
    ....
else # when directory is removed from /proc, process has ended
    wait $pid
    local exit_status=$?
done
....

Vous pouvez maintenant utiliser la variable $exit_status comme bon vous semble.

0 votes

Ne fonctionne pas dans bash ? Syntax error: "else" unexpected (expecting "done")

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X