Obter o PID de qualquer comando na sequência de comandos canalizada em segundo plano

11

Se, em bash, eu executar:

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

onde o cmd{1..n}pode não ser distinto, como obtenho o PID cmdi? Como alternativa, como posso sinalizar o cmdiprocesso? (Por exemplo, enviar SIGUSR1?) pkill/ pgrep, pidofEtc. não parecem boas respostas, pois outras instâncias cmdipodem estar em execução, inclusive como parte do mesmo pipeline. jobs -pdá o PID de cmd1, para mim.

ipode ser qualquer coisa {1..n}.

muru
fonte
possível duplicata de Como posso obter o pid de um processo iniciado dessa maneira
G-Man diz 'Reinstate Monica'
1
@ G-Man Care para explicar? Vejo apenas similaridade superficial e, como expliquei na resposta de Ramesh, modificar o conjunto de comandos não é de muita utilidade.
muru 18/09/14
Semelhança superficial? cat /var/run/out | nc -l 8080é apenas superficialmente semelhante a cmd1 | cmd2? Sua restrição de que você deseja digitar o pipeline de esqueleto e depois recuperar os PIDs (1) não é declarada na pergunta e (2) dificilmente permitirá uma solução boa e geral.
G-Man diz 'Reinstate Monica'
@ G-Man Pelo contrário, você está impondo restrições simples que não são declaradas. cmd1 | cmd2é um caso muito especial em que ambos os PIDs são facilmente obtidos. Eu disse alguma coisa sobre n? Então, por que você assumiria n = 2? Eu disse alguma coisa sobre o que é cmdi? Então, por que você acha que eu poderia modificar o cmdi? Estou pedindo uma solução geral e você está impondo restrições.
muru 18/09/14

Respostas:

6

Para a versão original da pergunta, quando apenas o PID do último comando foi desejado, a variável especial $!é perfeita.

foo | bar | baz &
baz_pid=$!

Não há acesso fácil semelhante aos PIDs dos outros processos.

Demorou muito tempo para adicionar $pipestatus(zsh) e $PIPESTATUS(bash), finalmente nos dando acesso a todos os status de saída em um pipeline, além $?do último que existe desde o shell Bourne original. Talvez algo análogo aconteça $!eventualmente.


fonte
Você se importaria se eu editasse a pergunta para solicitar também o PID de um comando arbitrário na lista? Ou devo começar uma nova pergunta?
muru 18/09/14
Você provavelmente terá que esperar muito mais tempo para responder a essa pergunta. Eu não tenho sentimentos fortes sobre organização do site Stackexchange então pergunta separada, editar pergunta, o que quer ... não vai me incomodar
Tudo bem, o problema imediato está resolvido, agora a curiosidade está no comando. Vou editá-lo então. Apenas um alerta, desde que eu vi as perguntas mudarem drasticamente e deixando as respostas mais antigas parecendo muito fora do lugar.
muru 18/09/14
@ muru - observe que seria melhor fazer um novo Q referenciando este.
SLM
@slm devidamente anotado. Fará isso no futuro.
muru 18/09/14
4

Eu acho que você poderia fazer algo como sugerido aqui .

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

Aqui no exemplo acima, eu recuperei o pid do terceiro processo canalizado e o anotei no pid do arquivo. Eu poderia anotá-lo para qualquer processo canalizado.

Ramesh
fonte
Interessante, mas isso envolveria a modificação do conjunto de comandos. Não é muito útil depois que os comandos foram executados.
muru 18/09/14
@muru - o que? que utilidade tem qualquer PID depois de terminar a execução? você quer o PID do pipeline? jobs -p. sinalize com SIGPIPE. Você quer cmdiisso?
mikeserv
1
@ mikeserv Não se eles estiverem em segundo plano, funcionando enquanto falamos. Com que feitiçaria devo modificar a linha de comando para isso?
muru 18/09/14
1
@ muru que seria uma feitiçaria. você precisa de um depurador.
mikeserv
Acho que esse é um padrão útil para iniciar processos em segundo plano, aguardando que eles atinjam algum estado e depois matando-os. No caso de alguém estiver interessado: gist.github.com/MatrixManAtYrService/...
MatrixManAtYrService
2

Uma solução específica para Linux, não muito portátil, pode ser rastrear os processos usando os pipes que os conectam. Podemos obter os PIDs do primeiro ( jobs -p) e do último ( $!) comando no pipeline. Usando um PID, este script pode fazer o trabalho:

#! /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
fonte
Para os curiosos, há mais sobre esse tipo de coisa aqui: unix.stackexchange.com/a/486233/146169
MatrixManAtYrService
0

Eu uso matrizes baseadas em zero aqui neste código. Apenas tome cuidado com o que você administra 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
fonte