Em:
cmd1 | cmd2
cmd2 não morre automaticamente quando cmd1 termina, no entanto, ele verá o final do arquivo em seu stdin. E é assim que os pipelines costumam chegar ao fim.
Em:
echo foo | sed s/o/e/g
sed será encerrado após echo ter terminado, porque descobre que não há mais a ler de seu stdin.
Você poderia tentar e ter cmd1 kill cmd2 na finalização, mas e se cmd2 não tivesse lido tudo que cmd1 escreveu no pipe ainda?
Se cmd2 morrer primeiro, cmd1 não morrerá automaticamente, mas será morto (com um SIGPIPE) na próxima vez que tentar gravar no canal (agora quebrado).
É assim que os pipelines gostam:
yes | head
terminate ( yes é eliminado após a primeira gravação que faz no pipe após head ter terminado depois de ler e imprimir as primeiras 10 linhas).
Agora, se você realmente quisesse matar processos na outra extremidade do canal, não há nenhuma maneira portátil de descobrir quais processos têm um descritor de arquivos para a outra extremidade de um canal.
No Linux, você pode procurar os arquivos em /proc/*/fd que têm o mesmo inode do pipe no fd até o final do pipe e onde as permissões do symlink em si indicam qual é o final do pipe.
$ (ls -lLi /proc/self/fd/3; ls -l /proc/self/fd/3) 3>&1 >&2 | (ls -Lil /proc/self/fd/0; ls -l /proc/self/fd/0)
224052 prw------- 1 chazelas chazelas 0 Sep 20 23:26 /proc/self/fd/3|
224052 prw------- 1 chazelas chazelas 0 Sep 20 23:26 /proc/self/fd/0|
l-wx------ 1 chazelas chazelas 64 Sep 20 23:26 /proc/self/fd/3 -> pipe:[224052]
lr-x------ 1 chazelas chazelas 64 Sep 20 23:26 /proc/self/fd/0 -> pipe:[224052]
O mesmo pipe inode e o fd até o fim da escrita tem w de permissões, enquanto o que está no final da leitura tem r permissões.
Por exemplo, com zsh você pode obter os pids dos processos que possuem um fd na extremidade de leitura de um canal cuja outra extremidade está em nosso fd 3 com:
pids=(/proc/<->/fd/*(Nf{u+r}e{'[[ $REPLY -ef /dev/fd/3 ]]'}-p:h:h:t))
pids=(${(u)pids}) # unique
Exemplo:
$ (pids=(/proc/<->/fd/*(Nf{u+r}e{'[[ $REPLY -ef /dev/fd/3 ]]'}-p:h:h:t))
pids=(${(u)pids}); ps -fp $pids) 3>&1 >&2 | tr a b
UID PID PPID C STIME TTY TIME CMD
chazelas 24157 11759 0 23:41 pts/1 00:00:00 tr a b
Então você pode fazer (ainda no Linux e com zsh ):
run_and_kill_the_other_end() {
setopt localoptions localtraps
trap : TERM
"$@"
local ret=$?
local -aU pids
if [ -p /dev/stdout ]; then
pids=(/proc/<2->/fd/<0-9>(Nf{u+r}e{'[[ $REPLY -ef /dev/stdout ]]'}-p:h:h:t))
exec >&- # give the right end a chance to see eof and act upon it
fi
[ -p /dev/stdin ] &&
pids+=(/proc/<2->/fd/<0-9>(Nf{u+w}e{'[[ $REPLY -ef /dev/stdin ]]'}-p:h:h:t))
(($#pids)) && kill $pids 2> /dev/null
return $ret
}
E então:
run_and_kill_the_other_end node a.js | run_and_kill_the_other_end node b.js
Mais uma vez, provavelmente não é isso que você quer fazer. Por exemplo:
$ run_and_kill_the_other_end seq 100 |
run_and_kill_the_other_end sort |
run_and_kill_the_other_end wc -l
0
sort foi morto antes de ter a chance de escrever sua saída classificada. wc -l conseguiu escapar.
Você pode inserir um atraso como período de tolerância, mas por quanto tempo deve ser o atraso? Por exemplo, se eu adicionar um sleep 0.1 após o exec >&- , vejo:
$ run_and_kill_the_other_end seq 100 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
100
$ run_and_kill_the_other_end seq 10000 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
10000
$ run_and_kill_the_other_end seq 100000 | run_and_kill_the_other_end sort | run_and_kill_the_other_end wc -l
0
Isso também significa atrasar o término do pipeline incondicionalmente. Você pode melhorar isso usando zsh de zselect com um tempo limite.