Attendre la fin de "n'importe quel processus"

y a-t-il une fonctionnalité intégrée à bash pour attendre la fin du processus? La commande wait permet seulement d'attendre la fin des processus enfants. Je voudrais savoir si il y a moyen d'attendre pour tout processus à terminer avant de passer dans n'importe quel script.

Une mécanique façon de le faire est comme suit, mais je voudrais savoir s'il est construit en fonction de bash.

while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done
102
demandé sur Dan D. 2009-06-29 16:33:28

13 réponses

il n'y a pas de bâtiment. Utilisez kill -0 dans une boucle pour une solution utilisable:

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 une fois:

while kill -0 PIDS 2> /dev/null; do sleep 1; done;

comme l'ont noté plusieurs commentateurs, si vous voulez attendre des processus auxquels vous n'avez pas le privilège 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 . Sur Linux, test -d "/proc/$pid" fonctionne, sur d'autres systèmes vous peut-être utiliser pgrep (si disponible) ou quelque chose comme ps | grep ^$pid .

69
répondu Teddy 2016-07-28 06:57:02

d'attendre la fin de n'importe quel processus

Linux:

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

Darwin (exige que $pid ait des fichiers ouverts):

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

Avec délai d'attente (en secondes)

Linux:

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

Darwin (exige 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)
51
répondu Rauno Palosaari 2017-01-12 12:57:00

j'ai trouvé "kill -0" ne fonctionne pas si le processus appartient à root (ou autre), donc j'ai utilisé pgrep et est venu avec:

while pgrep -u root process_name > /dev/null; do sleep 1; done

cela aurait l'inconvénient de correspondre probablement processus zombie.

47
répondu mp3foley 2012-05-02 04:52:19

cette boucle bash script se termine si le processus n'existe pas, ou si c'est 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 se basant sur procfs i.e. /proc/ . Je ne connais pas sa portabilité:

while [[ ( -d /proc/$PID ) && ( -z `grep zombie /proc/$PID/status` ) ]]; do
    sleep 1
done

ce n'est pas limité à shell, mais OS's ils n'ont pas eux-mêmes d'appels système pour surveiller la fin du processus sans enfant.

28
répondu teika kazura 2016-09-28 05:31:23

à Partir de la page de manuel de 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.
11
répondu Charlweed 2011-08-26 20:08:06

FreeBSD et Solaris ont cet utilitaire pratique pwait(1) , qui fait exactement ce que vous voulez.

je crois, d'autres OS modernes ont aussi les appels système nécessaires (MacOS, par exemple, met en œuvre BSD kqueue ), mais tous ne le rendent pas disponible en ligne de commande.

11
répondu Mikhail T. 2017-01-03 19:26:07

Ok, donc il semble que la réponse est -- non, il n'y a pas d'outil intégré.

après avoir réglé /proc/sys/kernel/yama/ptrace_scope sur 0 , il est possible d'utiliser le programme strace . D'autres interrupteurs peuvent être utilisés pour la rendre silencieuse, de sorte qu'elle attend vraiment passivement:

strace -qqe '' -p <PID>
5
répondu emu 2014-04-06 11:34:52

toutes ces solutions sont testées dans Ubuntu 14.04:

Solution 1 (en utilisant la commande ps): Juste 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 ne correspond qu'à process_name et pas à grep lui-même. Il a l'avantage de supporter les cas où le nom de processus n'est pas à la fin d'une ligne à ps axg .

Solution 2 (en utilisant la commande supérieure et le nom du procédé):

while [[ $(awk '=="process_name" {print "151910920"}' <(top -n 1 -b)) ]]; do sleep 1; done

remplacer process_name par le nom du procédé 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 lancer:

while : ; do p=$(awk '=="process_name" {print "151920920"}' <(top -n 1 -b)); [[ $b ]] || break; echo $p; sleep 1; done

Solution 3 (en utilisant la commande supérieure et le numéro de processus):

while [[ $(awk '=="process_id" {print "151930920"}' <(top -n 1 -b)) ]]; do sleep 1; done

remplacer process_id avec l'identifiant du processus de votre programme.

4
répondu Saeid BK 2016-07-03 21:42:25

j'ai trouvé un petit utilitaire qui attend sur n'importe quel PID et retourne immédiatement quand le PID 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 de la même manière qu'un débogueur quand il s'attache à un processus.

3
répondu Mark L. Smith 2016-08-30 15:32:41

il n'y a pas de fonctionnalité intégrée pour attendre la fin de n'importe quel processus.

vous pouvez envoyer des kill -0 à n'importe quel PID trouvé, de sorte que vous ne soyez pas déconcerté par les zombies et les choses qui seront encore visibles dans ps (tout en récupérant la liste PID en utilisant ps).

2
répondu TheBonsai 2009-06-29 17:19:03

sur un système comme OSX vous pourriez ne pas avoir pgrep donc vous pouvez essayer cette évaluation, en recherchant des processus par nom:

while ps axg | grep process_name$ > /dev/null; do sleep 1; done

le symbole $ à la fin du nom du processus garantit que grep ne correspond que process_name à la fin de la ligne dans la sortie ps et pas lui-même.

0
répondu Pierz 2015-02-03 13:48:18

avait le même problème, j'ai résolu le problème en tuant le processus et en attendant que chaque processus se termine en utilisant le système de fichiers PROC:

while [ -e /proc/${pid} ]; do sleep 0.1; done
0
répondu littlebridge 2017-06-14 11:03:24

solution de blocage

utiliser le wait dans une boucle, pour attendre terminer tous les processus:

function anywait()
{

    for pid in "$@"
    do
        wait $pid
        echo "Process $pid terminated"
    done
    echo 'All processes terminated'
}

cette fonction disparaîtra immédiatement, lorsque tous les processus auront été interrompus. C'est la solution la plus efficace.

Non-solution de blocage

utiliser le kill -0 dans une boucle, pour attendre terminer tous les processus + faire n'importe quoi entre les contrôles:

function anywait_w_status()
{
    for pid in "$@"
    do
        while kill -0 "$pid"
        do
            echo "Process $pid still running..."
            sleep 1
        done
    done
    echo 'All processes terminated'
}

le temps de réaction est passé à sleep parce qu'il faut éviter une forte utilisation de CPU.

un usage réaliste:

en Attente pour mettre fin à tous les processus + informer l'utilisateur sur tous running PIDs.

function anywait_w_status2()
{
    while true
    do
        alive_pids=()
        for pid in "$@"
        do
            kill -0 "$pid" 2>/dev/null \
                && alive_pids+="$pid "
        done

        if [ ${#alive_pids[@]} -eq 0 ]
        then
            break
        fi

        echo "Process(es) still running... ${alive_pids[@]}"
        sleep 1
    done
    echo 'All processes terminated'
}

Notes

ces fonctions reçoivent les PIDs via les arguments par $@ comme tableau de BASH.

-1
répondu andras.tim 2015-09-08 04:28:07