bash - usuario - ver procesos en ejecucion linux
Cómo obtener el PID de un proceso en proceso (8)
Mejorando las respuestas de @Marvin y @Nils Goroll con un delineador que extrae los pids de todos los comandos en una variable de matriz de shell:
# run some command
ls -l | rev | sort > /dev/null &
# collect pids
pids=(`jobs -l % | egrep -o ''^(/[[0-9]+/]/+| ) [ 0-9]{5} '' | sed -e ''s/^[^ ]* /+//'' -e ''s! $!!''`)
# use them for something
echo pid of ls -l: ${pids[0]}
echo pid of rev: ${pids[1]}
echo pid of sort: ${pids[2]}
echo pid of first command e.g. ls -l: $pids
echo pid of last command e.g. sort: ${pids[-1]}
# wait for last command in pipe to finish
wait ${pids[-1]}
¡En mi solución ${pids[-1]}
contiene el valor normalmente disponible en $!
. Tenga en cuenta el uso de jobs -l %
que genera solo el trabajo "actual", que por defecto es el último iniciado.
Muestra de salida:
pid of ls -l: 2725
pid of rev: 2726
pid of sort: 2727
pid of first command e.g. ls -l: 2725
pid of last command e.g. sort: 2727
ACTUALIZACIÓN 2017-11-13: Se mejoró el pids=...
que funciona mejor con comandos complejos (varias líneas).
Considere el siguiente ejemplo simplificado:
my_prog|awk ''...'' > output.csv &
my_pid="$!" #Gives the PID for awk instead of for my_prog
sleep 10
kill $my_pid #my_prog still has data in its buffer that awk never saw. Data is lost!
En bash, $my_pid
apunta al PID para awk
. Sin embargo, necesito el PID para my_prog
. Si my_prog
awk
, my_prog
no sabe descargar su búfer de salida y se pierden datos. Entonces, ¿cómo se obtendría el PID para my_prog
? Tenga en cuenta que ps aux|grep my_prog
no funcionará, ya que puede haber varios my_prog
en marcha.
NOTA: cambié cat
a awk ''...''
para ayudar a aclarar lo que necesito.
Agregue un envoltorio de shell alrededor de su comando y capture el pid. Para mi ejemplo, uso iostat.
#!/bin/sh
echo $$ > /tmp/my.pid
exec iostat 1
Exec reemplaza el shell con el nuevo proceso preservando el pid.
test.sh | grep avg
Mientras eso funciona:
$ cat my.pid
22754
$ ps -ef | grep iostat
userid 22754 4058 0 12:33 pts/12 00:00:00 iostat 1
Así que puedes:
sleep 10
kill `cat my.pid`
¿Es eso más elegante?
En función de su comentario, todavía no veo por qué prefiere matar a my_prog
para que se complete de manera ordenada. Diez segundos es una medida bastante arbitraria en un sistema de multiprocesamiento mediante el cual my_prog
podría generar 10k líneas o 0 líneas de salida dependiendo de la carga del sistema.
Si quieres limitar la salida de my_prog
a algo más determinado, prueba
my_prog | head -1000 | awk
sin desprenderse del caparazón. En el peor de los casos, head cerrará su entrada y my_prog obtendrá un SIGPIPE. En el mejor de los casos, cambie my_prog
para que le proporcione la cantidad de salida que desea.
agregado en respuesta al comentario :
En la medida en que tengas control sobre my_prog
dale un argumento de -s duration
opcional. Luego, en algún lugar de tu ciclo principal, puedes poner el predicado:
if (duration_exceeded()) {
exit(0);
}
donde la salida a su vez vaciará correctamente los ARCHIVOS de salida. Si está desesperado y no hay lugar para poner el predicado, esto podría implementarse usando la alarma (3), que intencionalmente no mostraré porque es malo.
El núcleo de tu problema es que my_prog
ejecuta para siempre. Todo lo demás aquí es un truco para superar esa limitación.
Pude resolverlo nombrando explícitamente la tubería con mkfifo
.
Paso 1: mkfifo capture
.
Paso 2: ejecuta este script
my_prog > capture &
my_pid="$!" #Now, I have the PID for my_prog!
awk ''...'' capture > out.csv &
sleep 10
kill $my_pid #kill my_prog
wait #wait for awk to finish.
No me gusta la gestión de tener un mkfifo. Con suerte, alguien tiene una solución más fácil.
Aquí hay una solución sin envoltorios o archivos temporales. Esto solo funciona para una canalización en segundo plano cuyo resultado se captura fuera de stdout del script que lo contiene, como en su caso. Supongamos que quieres hacer:
cmd1 | cmd2 | cmd3 >pipe_out &
# do something with PID of cmd2
Si solo bash pudiera proporcionar ${PIPEPID[n]}
!! El "truco" de reemplazo que encontré es el siguiente:
PID=$( { cmd1 | { cmd2 0<&4 & echo $! >&3 ; } 4<&0 | cmd3 >pipe_out & } 3>&1 | head -1 )
Si es necesario, también puede cerrar el fd 3 (para cmd*
) y fd 4 (para cmd2
) con 3>&-
y 4<&-
, respectivamente. Si haces eso, para cmd2
asegúrate de cerrar fd 4 solo después de redirigir fd 0 desde él.
Solo tuve el mismo problema. Mi solución:
process_1 | process_2 &
PID_OF_PROCESS_2=$!
PID_OF_PROCESS_1=`jobs -p`
Solo asegúrese de que el proceso_1 sea el primer proceso en segundo plano. De lo contrario, debe analizar la salida completa de jobs -l
.
Con la inspiración de la respuesta de @Demosthenex: usando subshells:
$ ( echo $BASHPID > pid1; exec vmstat 1 5 ) | tail -1 &
[1] 17371
$ cat pid1
17370
$ pgrep -fl vmstat
17370 vmstat 1 5
Estaba buscando desesperadamente una buena solución para obtener todos los PID de un trabajo de tuberías, y un enfoque prometedor falló miserablemente (ver revisiones previas de esta respuesta).
Por lo tanto, desafortunadamente, lo mejor que pude encontrar es analizar los jobs -l
salida usando GNU awk:
function last_job_pids {
if [[ -z "${1}" ]] ; then
return
fi
jobs -l | awk ''
/^/[/ { delete pids; pids[$2]=$2; seen=1; next; }
// { if (seen) { pids[$1]=$1; } }
END { for (p in pids) print p; }''
}