Comando Pipe to tail: quando o primeiro comando é abortado?

2

Neste exemplo:

$ for i in {1..3}; do sleep 1; echo $i;  done | head -n 2

por que o primeiro comando (loop) é eliminado apenas antes da exibição do 3 ? Eu esperava que fosse morto logo após o 2 ser exibido.

O problema que eu estava tentando resolver inicialmente era esse:

$ for i in *(/); do c='find "$i" -iname "*.ext" | wc -l'; echo "$c  $i"; done | head -n 3

Eu esperava que terminasse assim que três linhas estivessem na tela, mas ele está executando o loop quatro vezes, descartando a quarta linha.

O que eu pensei que aconteceria:

sleep 1
echo 1
sleep 1
echo 2 
# 2 is printed and the for loop is killed

O que realmente acontece:

sleep 1
echo 1
sleep 1
echo 2 
sleep 1
echo 3
# 3 is not printed and the for loop is killed

Existe uma maneira de abortar o loop imediatamente após um determinado número de iterações sem adicionar alguma lógica dentro do próprio loop?

    
por alecail 12.11.2013 / 09:36

2 respostas

1

Um SIGPIPE é enviado somente quando um programa tenta gravar em um canal fechado. Seria muito ruim enviar SIGPIPE de outra forma: programas que têm um pipe aberto podem nem estar cientes disso, e não devem ser mortos se nunca interagirem com o pipe.

Em for i in 1 2 3; do sleep 1; echo $i; done , o comando sleep não escreve nada, por isso não receberá um sinal. É somente quando echo é executado que um SIGPIPE é enviado.

Em geral, o SIGPIPE não é projetado para trabalhos de precisão. Isso só serve para garantir que os programas cujo trabalho é produzir alguma saída não continuem correndo para sempre quando o consumidor dessa saída for embora. Na maioria dos cenários práticos, o programa no lado esquerdo dos buffers de pipe grava e só receberá um SIGPIPE na próxima vez que esvaziar seu buffer.

Se você quiser interromper um loop após várias iterações, crie essa lógica no loop.

    
por 13.11.2013 / 01:42
2

Em minhas diferentes shells (ksh, zsh, bash), eu tenho o bahaviour esperado: eu só tenho 1, 2, não 3. O problema aqui pode estar relacionado a como o SIGPIPE é tratado em sua versão ZSH.

Você pode tentar executar esses comandos em uma instância "env-free"? por exemplo, sem alias, configuração, etc ...

Como colocado nos comentários, o shell recebe um erro quando deseja fazer eco de algo (com a função write ()). Contanto que toda a cadeia de tubos esteja em execução (o processo head ainda está em execução e tenha o STDIN aberto), tudo corre bem. Mas quando o head terminar, a saída do seu shell não chegará a lugar algum (tubo quebrado).

Mas até que o shell tente escrever algo em sua stdout, ele não está ciente de que o pipe está quebrado e continua seu processamento.

Assim, você verá o "echo 3" na depuração, que falhará e terminará o shell.

    
por 12.11.2013 / 09:44