bash - script - ESPERE a que termine "cualquier proceso"
bash wait for python script to finish (13)
Solución de bloqueo
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 bloqueo
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.
¿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
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)
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 programa de strace
. Se pueden usar conmutadores adicionales para silenciarlo, de modo que realmente espere pasivamente:
strace -qqe '''' -p <PID>
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.
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.
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.
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 compilar con 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);
}
Funciona de la misma manera que un depurador cuando se une a un proceso.
Este ciclo de script bash finaliza si el proceso no existe o si es un zombie.
PID=<pid to watch>
while s=`ps -p $PID -o s=` && [[ "$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.
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.
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
.
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).
Todas estas soluciones se prueban en Ubuntu 14.04:
Solución 1 (usando el comando ps): solo para sumar 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 programa.
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