Por que usar 'yes' nos pipelines bash * não * causa loops infinitos?

15

De acordo com sua documentação, o bash aguarda até que todos os comandos em um pipeline tenham terminado a execução antes de continuar

The shell waits for all commands in the pipeline to terminate before returning a value.

Então, por que o comando yes | true é concluído imediatamente? O yes não deve ser repetido para sempre e fazer com que o pipeline nunca retorne?

E uma subquestão: de acordo com a especificação POSIX , os pipelines de shell podem escolher retorne após o último comando terminar ou aguarde até que todos os comandos sejam concluídos. As cascas comuns têm comportamento diferente nesse sentido? Há algum shells em que yes | true faça um loop para sempre?

    
por hugomg 16.11.2015 / 17:07

2 respostas

32

Quando true sai, o lado de leitura do canal é fechado, mas yes continua tentando gravar no lado da gravação. Essa condição é chamada de "pipe corrompido" e faz com que o kernel envie um sinal SIGPIPE para yes . Como yes não faz nada especial sobre este sinal, ele será eliminado. Se ele ignorasse o sinal, sua chamada write falharia com o código de erro EPIPE . Programas que fazem isso precisam estar preparados para notar EPIPE e parar de escrever, ou eles entrarão em um loop infinito.

Se você usar strace yes | true 1 , poderá ver o kernel se preparando para as duas possibilidades:

write(1, "y\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\ny\n"..., 4096) = -1 EPIPE (Broken pipe)
--- SIGPIPE {si_signo=SIGPIPE, si_code=SI_USER, si_pid=17556, si_uid=1000} ---
+++ killed by SIGPIPE +++

strace está assistindo eventos por meio da API do depurador, que primeiro informa sobre a chamada do sistema que retorna com um erro e, em seguida, sobre o sinal. Do ponto de vista de yes , porém, o sinal acontece primeiro. (Tecnicamente, o sinal é entregue depois que o kernel retorna o controle para o espaço do usuário, mas antes que mais instruções da máquina sejam executadas, a função write "wrapper" na biblioteca C não tem a chance de definir errno e retornar para a aplicação.)

1 Infelizmente, strace é específico do Linux. A maioria dos Unixes modernos tem o comando some que faz algo semelhante, mas geralmente tem um nome diferente, provavelmente não decodifica argumentos syscall tão detalhadamente, e às vezes funciona apenas para root.

    
por 16.11.2015 / 17:14
5

Are there any shells where yes | true will loop forever?

Não é provável, pois o comando yes está usando o pipe e falhará quando o pipe for quebrado. sleep , por outro lado, não usa o pipe, então:

sleep 100000000 | true

será executado por pelo menos 100000000 segundos.

    
por 16.11.2015 / 17:24

Tags