Duas janelas, mesmo usuário, com prompts do bash. Na janela-1 digite:
$ mkfifo f; exec <f
Portanto, o bash agora está tentando ler do descritor de arquivo 0, que é mapeado para pipe nomeado f
. Na janela-2 digite:
$ echo ls > f
Agora window-1 imprime um ls e então o shell morre. Por quê?
Próxima experiência: abra a janela-1 novamente com exec <f
. Na janela-2 digite:
$ exec 3>f
$ echo ls >&3
Após a primeira linha acima, a janela-1 acorda e imprime um prompt. Por quê? Após a segunda linha acima, a janela-1 imprime a ls
saída e o shell permanece ativo. Por quê? Na verdade, agora na janela-2, echo ls > f
não fecha o shell da janela-1.
A resposta deve ter a ver com a existência do descritor de arquivo 3 da janela-2 referenciando o pipe nomeado?!
Tem a ver com o fechamento do descritor de arquivo.
Em seu primeiro exemplo,
echo
grava em seu fluxo de saída padrão que o shell abre para conectá-lof
e, quando termina, seu descritor é fechado (pelo shell). Na extremidade receptora, o shell, que lê a entrada de seu fluxo de entrada padrão (conectado af
) lêls
, executals
e termina devido à condição de fim de arquivo em sua entrada padrão.A condição de fim de arquivo ocorre porque todos os gravadores no pipe nomeado (apenas um neste exemplo) fecharam sua extremidade do pipe.
Em seu segundo exemplo,
exec 3>f
abre o descritor de arquivo 3 para gravar emf
, depoisecho
gravals
nele. É o shell que agora tem o descritor de arquivo aberto, não oecho
comando. O descritor permanece aberto até que você o façaexec 3>&-
. Na extremidade receptora, o shell, que lê a entrada de seu fluxo de entrada padrão (conectado af
) lêls
, executals
e aguarda mais entradas (já que o fluxo ainda está aberto).O fluxo permanece aberto porque todos os escritores para ele (o shell, via
exec 3>f
eecho
) não fecharam sua extremidade do canal (exec 3>f
ainda está em vigor).Eu escrevi
echo
acima como se fosse um comando externo. É mais provável que esteja embutido no shell. O efeito é o mesmo, no entanto.Não há muito para isso: quando não há escritores no pipe, ele parece fechado para os leitores, ou seja, retorna EOF quando lido e bloqueia quando aberto.
Na página de manual do Linux (
pipe(7)
, mas veja tambémfifo(7)
):Fechar o final de gravação é o que acontece implicitamente no final do
echo ls >f
, e como você diz, no outro caso, o descritor de arquivo é mantido aberto.Depois de ler as duas respostas de @Kusalananda e @ikkachu, acho que entendi. Na janela-1, o shell está esperando por algo para abrir a extremidade de gravação do pipe e depois fechá-lo. Uma vez que o final de gravação é aberto, o shell na janela-1 imprime um prompt. Uma vez que o final da gravação é fechado, o shell recebe EOF e morre.
No lado da janela 2, temos as duas situações descritas na minha pergunta: na primeira situação com
echo ls > f
, não há descritor de arquivo 3, então temosecho
spawning, e éstdin
assimstdout
:Em seguida,
echo
termina e o shell fecha ambos os descritores. Como o descritor de arquivo 1 está fechado e referênciasf
, o final de gravação def
é fechado e isso faz com que um EOF seja janela-1.Na segunda situação, rodamos
exec 3>f
em nosso shell, fazendo com que o shell tome este ambiente:Agora executamos
echo ls >& 3
e o shell aloca descritores de arquivo daecho
seguinte forma:Em seguida, o shell fecha os três descritores acima, incluindo
f
, masf
ainda tem uma referência a ele do próprio shell. Esta é a diferença importante. Fechar o descritor 3 comexec 3>&-
fecharia a última referência aberta e causaria um EOF na janela-1, como observou @Kusalananda.