web-dev-qa-db-fra.com

ATTENDRE que "n'importe quel processus" se termine

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
107
Biswajyoti Das

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.

74
Teddy

Attendre la fin d'un processus

Linux:

tail --pid=$pid -f /dev/null

Darwin (nécessite que $pid ait des fichiers ouverts):

lsof -p $pid +r 1 &>/dev/null

Avec timeout (secondes)

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)
65
Rauno Palosaari

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.

48
mp3foley

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.

28
teika kazura

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.

11
Mikhail T.

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.
10
Charlweed

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.

5
Saeid BK

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>
5
emu

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.

3
Mark L. Smith

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).

2
TheBonsai

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
0
littlebridge

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.

0
Pierz