ESPERE a que termine “cualquier proceso”

¿Hay alguna función incorporada en bash para esperar a que finalice algún proceso? El comando de wait solo permite que uno espere a que los procesos secundarios finalicen. Me gustaría saber si hay alguna manera de esperar a que termine algún proceso antes de continuar en cualquier script.

Una forma mecánica de hacer esto es la siguiente, pero me gustaría saber si hay alguna característica incorporada en bash.

 while ps -p `cat $PID_FILE` > /dev/null; do sleep 1; done 

No hay construcción interna. Use kill -0 en un bucle para una solución viable:

 anywait(){ for pid in "$@"; do while kill -0 "$pid"; do sleep 0.5 done done } 

O como un delineador más simple para un fácil uso de una sola vez:

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

Como señalaron varios comentaristas, si desea esperar procesos a los que no tiene el privilegio de enviar señales, ha encontrado otra forma de detectar si el proceso se está ejecutando para reemplazar la llamada kill -0 $pid . En Linux, la test -d "/proc/$pid" funciona, en otros sistemas puede que tenga que usar pgrep (si está disponible) o algo así como ps | grep ^$pid ps | grep ^$pid .

Esperar a que termine cualquier proceso

Linux:

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

Darwin (requiere que $pid tenga archivos abiertos):

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

Con tiempo de espera (segundos)

Linux:

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

Darwin (requiere que $pid tenga archivos abiertos):

 lsof -p $pid +r 1m%s -t | grep -qm1 $(date -v+${timeout}S +%s 2>/dev/null || echo INF) 

Encontré que “kill -0” no funciona si el proceso es propiedad de root (u otro), así que usé pgrep y se me ocurrió:

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

Esto tendría la desventaja de que probablemente coincida con los procesos zombies.

Este ciclo de script bash finaliza si el proceso no existe o si es un zombie.

 PID= while s=`ps -p $PID -os=` && [[ "$s" && "$s" != 'Z' ]]; do sleep 1 done 

EDITAR : El guión anterior fue dado a continuación por Rockallite . ¡Gracias!

Mi respuesta orignal a continuación funciona para Linux, confiando en procfs ie /proc/ . No sé su portabilidad:

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

No está limitado a shell, pero los propios sistemas operativos no tienen llamadas al sistema para ver la finalización del proceso no hijo.

Desde la página de manual 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. 

FreeBSD y Solaris tienen esta útil utilidad pwait(1) , que hace exactamente lo que usted quiere.

Creo que otros sistemas operativos modernos también tienen las llamadas al sistema necesarias (MacOS, por ejemplo, implementa kqueue de BSD), pero no todos lo hacen disponible desde la línea de comandos.

De acuerdo, parece que la respuesta es: no, no hay una herramienta integrada.

Después de configurar /proc/sys/kernel/yama/ptrace_scope en 0 , es posible utilizar el progtwig de strace . Se pueden usar conmutadores adicionales para silenciarlo, de modo que realmente espere pasivamente:

 strace -qqe '' -p  

Todas estas soluciones se prueban en Ubuntu 14.04:

Solución 1 (usando el comando ps): solo para sumr a la respuesta de Pierz, sugeriría:

 while ps axg | grep -vw grep | grep -w process_name > /dev/null; do sleep 1; done 

En este caso, grep -vw grep asegura que grep coincida solo con process_name y no grep. Tiene la ventaja de admitir los casos donde el nombre_proceso no está al final de una línea en ps axg .

Solución 2 (usando el comando superior y el nombre del proceso):

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

Reemplace process_name con el nombre del proceso que aparece en la top -n 1 -b . Por favor, mantenga las comillas.

Para ver la lista de procesos que espera que finalicen, puede ejecutar:

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

Solución 3 (usando el comando superior y la ID del proceso):

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

Reemplace process_id con la identificación del proceso de su progtwig.

Encontré una pequeña utilidad que espera en cualquier PID y regresa inmediatamente cuando el PID muere.

https://github.com/izabera/waiter

Es un pequeño archivo C que puedes comstackr con gcc.

 #define _GNU_SOURCE #include  #include  #include  #include  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); } 

Funciona de la misma manera que un depurador cuando se une a un proceso.

No hay ninguna función integrada que esperar a que termine ningún proceso.

Podrías enviar kill-0 a cualquier PID encontrado, para que no te dejes confundir por los zombis y cosas que todavía serán visibles en ps (mientras recuperas la lista de PID usando ps).

En un sistema como OSX, es posible que no tenga pgrep, por lo que puede probar esta aplicación cuando busque procesos por nombre:

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

El símbolo $ al final del nombre del proceso asegura que grep solo coincide con el nombre del proceso al final de la línea en la salida ps y no a sí mismo.

Tuve el mismo problema, resolví el problema matando el proceso y luego esperando que cada proceso termine de usar el sistema de archivos PROC:

 while [ -e /proc/${pid} ]; do sleep 0.1; done 

Solución de locking

Use la wait en un bucle, para esperar la finalización de todos los procesos:

 function anywait() { for pid in "$@" do wait $pid echo "Process $pid terminated" done echo 'All processes terminated' } 

Esta función saldrá inmediatamente, cuando todos los procesos hayan terminado. Esta es la solución más eficiente.

Solución sin locking

Use kill -0 en un bucle, para esperar a terminar todos los procesos + hacer cualquier cosa entre cheques:

 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' } 

El tiempo de reacción disminuyó al tiempo de sleep , debido a que tiene que evitar un uso elevado de la CPU.

Un uso realista:

Esperando finalizar todos los procesos + informar al usuario sobre todos los PID en ejecución.

 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' } 

Notas

Estas funciones obtienen PID a través de argumentos de $@ como BASH array.