Corridas ao enviar dois comandos para um pipe nomeado

5

Eu quero ter uma leitura de processo de um pipe nomeado que recebe dados de várias origens:

$ mkfifo /tmp/p

Mas não consigo descobrir como fazer isso funcionar de forma consistente.

Primeiro cenário - isso funciona

tty1 :

Configure dois processos para escrever no meu fifo; ambos irão bloquear:

$ echo 'first' > /tmp/p; echo 'second' > /tmp/p

tty2 :

Ler do canal:

$ cat /tmp/p
first
second

Isso ainda funciona se eu executar o acima na ordem inversa

Meu problema surge quando tenho dois comandos separados que quero sair do canal:

Segundo cenário - não funciona

first.sh

#!/bin/sh
echo 'first' > /tmp/p

second.sh

#!/bin/sh
echo 'second' > /tmp/p

tty1

$ sh first.sh; sh second.sh

tty2

$ cat /tmp/p
first

A execução de sh second.sh do meu primeiro tty será bloqueada indefinidamente até que alguma outra coisa seja lida no pipe nomeado.

O que eu acho que está acontecendo

De link :

If all file descriptors referring to the write end of a pipe have been closed, then an attempt to read(2) from the pipe will see end-of-file (read(2) will return 0)

Então, quando echo sai em first.sh , o shell que está executando fecha o descritor de arquivo para /tmp/p , o que significa que cat no segundo TTY vê EOF.

Como faço para contornar isso com o shell? Existe uma maneira de manter uma referência ao final de leitura do pipe nomeado no meu script de controle principal, para que ele não seja fechado quando as sub-shells forem encerradas? Na prática, estarei passando o caminho para o pipe nomeado para as sub-shells. Preciso apenas fazer meus subshells saírem para seu próprio stdout e executar um redirecionamento neles?

Eu sinto que há algo que sinto falta aqui. Usar pipes nomeados foi simples e direto para tudo que tentei fazer além deste caso.

    
por Cera 24.03.2013 / 09:03

4 respostas

6

Por que não apenas:

{ echo foo; echo bar;} > /tmp/p

Se você quiser que o script de controle deixe o canal aberto, faça o seguinte:

exec 3<> /tmp/p

Abrir um pipe nomeado no modo de leitura-gravação é evitar que seja bloqueado se o pipe ainda não tiver sido aberto. Isso iria instanciar se ainda não fosse. Ele funciona no Linux, pelo menos, mas não é garantido pelo POSIX.

Como alternativa (e portável):

: < /tmp/p & exec 3> /tmp/p

Então, em vez de cada processo abrir o pipe nomeado, você também pode fazer:

cmd >&3

E no final, você faria:

exec 3>&-

Para fechar o fim da escrita, para que os leitores saibam que está terminado.

altere todos os < s para > s e < s para > s se você precisar que a lógica seja o contrário.

    
por 24.03.2013 / 17:20
2

Você não pode usar o cat para leitura de pipes nomeados como você está descobrindo. Eu tive exatamente o mesmo problema há alguns anos, e escrevi pcat para superar essa limitação.

    
por 24.03.2013 / 11:27
0

Você apenas não entendeu como funciona o gato. A saída do eco faz com que o gato saia também. Então, se você não pode forçar seus processos de escrita para abrir o pipe rw (e mantê-lo aberto), então você simplesmente tem que chamar cat em um loop:

while true; do cat /tmp/p || break; done

o intervalo torna-se ativo quando você cancela o gato com, e. ^ C.

    
por 24.03.2013 / 20:30
0

É possível manter manualmente uma referência de pseudo-escrita ao fifo (usando tail -f /dev/null 1>fifo ou cat fifo 3>fifo ) para que o final de leitura do fifo não seja fechado após a primeira leitura.

# tty1
bash -c '
mkfifo fifo || exit 1
tail -f /dev/null 1>fifo &
#0<&- cat fifo 3>fifo &
#lsof -p $!
echo first > fifo
echo second > fifo
kill $!
'

# tty2
cat fifo
    
por 22.03.2016 / 18:15

Tags