Obtenga el PID de cualquier comando en una secuencia de comandos canalizada en segundo plano

11

Si, en bash, ejecuto:

cmd1 | cmd2 | ... | cmdi | ... | cmdn &

donde cmd{1..n}puede no ser distinto, ¿cómo obtengo el PID cmdi? Alternativamente, ¿cómo puedo señalar el cmdiproceso? (Por ejemplo, ¿enviarlo SIGUSR1?) pkill/ pgrep, pidofEtc. no parecen buenas respuestas, ya que otras instancias de cmdiejecución tal vez, incluso como parte de la misma canalización. jobs -pda el PID de cmd1, para mí.

ipuede ser cualquier cosa adentro {1..n}.

muru
fuente
posible duplicado de ¿Cómo puedo obtener el pid de un proceso iniciado de esta manera?
G-Man dice 'Reinstate Monica'
1
@ G-Man Care para explicar? Solo veo similitudes superficiales, y como expliqué en la respuesta de Ramesh, modificar el conjunto de comandos no es de mucha utilidad.
muru
¿Similitud superficial? cat /var/run/out | nc -l 8080es solo superficialmente similar a cmd1 | cmd2? Su restricción, que desea escribir la tubería básica y luego recuperar los PID, (1) no se indica en la pregunta, y (2) es poco probable que permita una buena solución general.
G-Man dice 'reinstalar a Monica' el
@ G-Man Por el contrario, está imponiendo restricciones simples que no se mencionan. cmd1 | cmd2Es un caso muy especial donde ambos PID son fácilmente obtenibles. ¿Dije algo sobre n? Entonces, ¿por qué asumirías n = 2? ¿Dije algo sobre lo que es cmdi? Entonces, ¿por qué asumirías que podría modificar cmdi? Estoy pidiendo una solución general y usted está imponiendo restricciones.
muru

Respuestas:

6

Para la versión original de la pregunta, cuando solo se deseaba el PID del último comando, la variable especial $!es perfecta.

foo | bar | baz &
baz_pid=$!

No hay un acceso fácil similar a los PID de los otros procesos.

Tomó mucho tiempo agregar $pipestatus(zsh) y $PIPESTATUS(bash), finalmente nos dio acceso a todos los estados de salida en una tubería, además $?del último que ha existido desde el shell Bourne original. Quizás con el $!tiempo sucederá algo análogo .


fuente
¿Te importaría si editara la pregunta para pedir el PID de un comando arbitrario en la lista también? ¿O debería comenzar una nueva pregunta?
muru
Probablemente tendrá que esperar mucho más para obtener una respuesta a esa pregunta. No tengo fuertes sentimientos acerca de la organización del sitio StackExchange por lo pregunta separada, editar cuestión, lo que sea ... no me moleste
Está bien, el problema inmediato está resuelto, ahora la curiosidad está a cargo. Lo editaré entonces. Solo un aviso, ya que he visto que las preguntas cambian drásticamente y dejan las respuestas anteriores muy fuera de lugar.
muru
@muru: tenga en cuenta que hubiera sido mejor convertirlo en una nueva Q que haga referencia a esta.
slm
@slm debidamente anotado. Lo haré en el futuro.
muru
4

Creo que podrías hacer algo como se sugiere aquí .

(ls -l | echo "Hello" | df -h & echo $! >&3 ) 3>pid

Aquí, en el ejemplo anterior, recuperé el pid del tercer proceso canalizado y lo anoté en el archivo pid. Podría anotarlo para cualquier proceso canalizado.

Ramesh
fuente
Interesante, pero esto implicaría modificar el conjunto de comandos. No es de gran utilidad una vez que se han ejecutado los comandos.
muru
@muru: ¿qué? ¿De qué sirve un PID una vez que ha finalizado la ejecución? ¿Quieres el PID de la tubería? jobs -p. señalarlo con SIGPIPE. ¿Quieres cmdiesto?
mikeserv
1
@mikeserv No si están en segundo plano, corriendo mientras hablamos. ¿Por qué hechicería se supone que modifique la línea de comando para eso?
muru
1
@muru eso sería una brujería. Necesitas un depurador.
mikeserv
Considero que este es un patrón útil para iniciar procesos en segundo plano, esperar a que alcancen algún estado y luego matarlos. En caso de que alguien esté interesado: gist.github.com/MatrixManAtYrService/…
MatrixManAtYrService
2

Una solución no muy portátil y específica de Linux podría ser rastrear los procesos utilizando las tuberías que los conectan. Podemos obtener los PID de los comandos first ( jobs -p) y last ( $!) en la tubería. Usando cualquiera de los PID, este script podría hacer el trabajo:

#! /bin/bash

PROC=$1
echo $PROC

if [[ $(readlink /proc/$PROC/fd/1) =~ ^pipe: ]]
then
    # Assuming first process in chain...
    NEXT_FD=1
elif [[ $(readlink /proc/$PROC/fd/0) =~ ^pipe: ]]
then
    # Last process in chain...
    NEXT_FD=0
else
    # Doesn't look like a pipe.
    exit
fi

NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)

while [[ $NEXT_PROC_PIPE =~ ^pipe: ]] 
do
    PROC=$(find /proc/*/fd -type l -printf "%p/%l\n" 2>/dev/null | awk -F'/' '($6 == "'"$NEXT_PROC_PIPE"'") && ($3 != "'$PROC'" ) {print $3}')
    NEXT_PROC_PIPE=$(readlink /proc/$PROC/fd/$NEXT_FD)
    echo $PROC
done
muru
fuente
Para los curiosos, hay más sobre este tipo de cosas aquí: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService
0

Yo uso matrices de base cero aquí en este código. Solo ten cuidado con lo que corres eval.

#!/bin/bash

cmd=('sleep 10' 'sleep 2' 'sleep 5')
first=1
for c in "${cmd[@]}"; do
  ((first)) && { pipe=$c; first=0; } || pipe+='|'$c
done
shopt -u lastpipe
eval $pipe &

printf 'Pipe:\n%s\n\n' "$pipe"

shellpid=$BASHPID
parent=$(ps -o pid= --ppid $shellpid | head -n -1)
declare -a pids=()
mapfile -t pids < <(printf '%s\n' $(ps -o pid= --ppid $parent))
printf '%s\n' 'Listing the arrays:'
printf '%2s %6s %s\n' i PID command
for i in "${!cmd[@]}"; do
    printf '%2d %6d %s\n' "$i" "${pids[i]}" "${cmd[i]}"
done

printf '\n%s\n' 'ps listing:'
ps xao pid,ppid,command | head -n 1
ps xao pid,ppid,command | tail | head -n -3
jarno
fuente