Existe-t-il une fonctionnalité intégrée dans bash permettant d’attendre la fin d’un processus? La commande wait
permet uniquement d’attendre la fin du processus enfant . processus pour terminer avant de procéder dans n'importe quel script.
Voici un moyen mécanique de le faire, mais j'aimerais savoir s'il existe une fonctionnalité intégrée dans bash.
while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
Il n'y a pas intégré. Utilisez kill -0
dans une boucle pour une solution viable:
anywait(){
for pid in "$@"; do
while kill -0 "$pid"; do
sleep 0.5
done
done
}
Ou comme un oneliner plus simple pour une utilisation facile en une fois:
while kill -0 PIDS 2> /dev/null; do sleep 1; done;
Comme l'ont noté plusieurs commentateurs, si vous souhaitez attendre des processus pour lesquels vous n'avez pas le droit d'envoyer des signaux, vous devez trouver un autre moyen de détecter si le processus est en cours d'exécution pour remplacer l'appel kill -0 $pid
. Sous Linux, test -d "/proc/$pid"
fonctionne, sur d’autres systèmes, vous devrez peut-être utiliser pgrep
(si disponible) ou quelque chose comme ps | grep ^$pid
.
Linux:
tail --pid=$pid -f /dev/null
Darwin (nécessite que $pid
ait des fichiers ouverts):
lsof -p $pid +r 1 &>/dev/null
Linux:
timeout $timeout tail --pid=$pid -f /dev/null
Darwin (nécessite que $pid
ait des fichiers ouverts):
lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF)
J'ai trouvé que "kill -0" ne fonctionnait pas si le processus appartenait à root (ou à un autre), alors j'ai utilisé pgrep et j'ai trouvé:
while pgrep -u root process_name > /dev/null; do sleep 1; done
Cela aurait l'inconvénient de faire probablement correspondre les processus zombies.
Cette boucle de script bash se termine si le processus n'existe pas ou s'il s'agit d'un zombie.
PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$s" && "$s" != 'Z' ]]; do
sleep 1
done
EDIT: Le script ci-dessus a été donné ci-dessous par Rockallite . Merci!
Ma réponse originale ci-dessous fonctionne pour Linux en s'appuyant sur procfs
c'est-à-dire /proc/
. Je ne connais pas sa portabilité:
while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
sleep 1
done
Cela ne se limite pas à Shell, mais les systèmes d'exploitation eux-mêmes ne disposent pas d'appels système leur permettant de surveiller la fin du processus non enfant.
FreeBSD et Solaris ont cet utilitaire pratique pwait(1)
, qui fait exactement ce que vous voulez.
Je crois que d’autres systèmes d’exploitation modernes disposent également des appels système nécessaires (MacOS, par exemple, implémente la variable kqueue
de BSD), mais tous ne la rendent pas disponible en ligne de commande.
De la page de manuel bash
wait [n ...]
Wait for each specified process and return its termination status
Each n may be a process ID or a job specification; if a
job spec is given, all processes in that job's pipeline are
waited for. If n is not given, all currently active child processes
are waited for, and the return status is zero. If n
specifies a non-existent process or job, the return status is
127. Otherwise, the return status is the exit status of the
last process or job waited for.
Toutes ces solutions sont testées dans Ubuntu 14.04:
Solution 1 (en utilisant la commande ps): .__ Pour ajouter à la réponse de Pierz, je suggérerais:
while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done
Dans ce cas, grep -vw grep
garantit que grep correspond uniquement à nom_processus et non à grep lui-même. Cela présente l'avantage de prendre en charge les cas où le nom_processus n'est pas en fin de ligne à ps axg
.
Solution 2 (en utilisant la commande top et le nom du processus):
while [[ $(awk '$12=="process_name" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done
Remplacez process_name
par le nom du processus qui apparaît dans top -n 1 -b
. Veuillez conserver les guillemets.
Pour voir la liste des processus que vous attendez qu'ils soient terminés, vous pouvez exécuter:
while : ; do p=$(awk '$12=="process_name" {print $0}' <(top -n 1 -b)); [[ $b ]] || break; echo $p; sleep 1; done
Solution 3 (en utilisant la commande supérieure et l'ID de processus):
while [[ $(awk '$1=="process_id" {print $0}' <(top -n 1 -b)) ]]; do sleep 1; done
Remplacez process_id
par l'ID de processus de votre programme.
D'accord, il semble que la réponse soit non, il n'y a pas d'outil intégré.
Après avoir défini /proc/sys/kernel/yama/ptrace_scope
sur 0
, il est possible d'utiliser le programme strace
. D'autres commutateurs peuvent être utilisés pour le rendre silencieux, de sorte qu'il attend réellement de manière passive:
strace -qqe '' -p <PID>
J'ai trouvé un petit utilitaire qui attend tout PID et revient immédiatement lorsque celui-ci meurt.
https://github.com/izabera/waiter
C'est un petit fichier C que vous pouvez compiler avec gcc.
#define _GNU_SOURCE
#include <sys/ptrace.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
if (argc == 1) return 1;
pid_t pid = atoi(argv[1]);
if (ptrace(PTRACE_SEIZE, pid, NULL, NULL) == -1) return 1;
siginfo_t sig;
return waitid(P_PID, pid, &sig, WEXITED|WNOWAIT);
}
Cela fonctionne comme un débogueur s’il s’attache à un processus.
Il n’existe pas de fonction intégrée permettant d’attendre la fin d’un processus.
Vous pouvez envoyer kill -0
à n’importe quel PID trouvé, pour ne pas être dérouté par des zombies et des objets qui seront toujours visibles dans ps
(tout en récupérant la liste PID avec ps
).
Si j'avais le même problème, j'ai résolu le problème en interrompant le processus et en attendant que chaque processus se termine avec le système de fichiers PROC:
while [ -e /proc/${pid} ]; do sleep 0.1; done
Sur un système comme OSX, vous pouvez ne pas avoir pgrep, vous pouvez donc essayer cette méthode lorsque vous recherchez des processus par leur nom:
while ps axg | grep process_name$ > /dev/null; do sleep 1; done
Le symbole $
à la fin du nom du processus garantit que grep correspond uniquement à nom_processus à la fin de la ligne dans la sortie ps et non à lui-même.