Obtendo stdin de um pipe nomeado

Obtendo stdin de um pipe nomeado

O que estou tentando fazer é executar o python em uma janela de terminal e redirecionar seu stdin de um pipe nomeado. Então escrevo no pipe nomeado em outro terminal e executo esse comando em python.

Terminal 1:

mkfifo p1
python < p1

Terminal 2:

echo -n "print \"Hello World\"" > p1

O que acontece é - python imprime Hello Worlde sai. O que eu quero fazer é manter o python em execução para executar o próximo comando. Como faço isso no shell?

Responder1

Você precisa

  • Execute python interativamente mesmo que seu stdin não seja um terminal: usepython -i
  • mantenha a extremidade de escrita do canal aberta, caso contrário, o python detectará EOF e sairá.

Então:

python -i < p1

E em outro lugar:

# open p1 on fd 3 of the shell process¹ for instance so stdout is
# left untouched in case you still want to write messages there.
exec 3> p1

# write something on fd 3. For instance with ksh/zsh's print
# builtin:
print -u3 '1j*1j'

# or for commands such as echo that can only print things
# on stdout (fd 1), duplicate that fd 3 to their fd 1:
echo '1j*1j' >&3 # short for 1>&3

# after echo (a builtin² in virtually all shells) returns, fd 1
# is restored to what it was before, but fd 3 remains open on
# the writing end of the pipe, so python still doesn't see EOF
# there.

echo normal message on stdout
echo 1+1 >&3 # more instructions for python
...
# and when done, close that file descriptor so python sees the EOF
# (assuming that fd 3 was the only file descriptor left open on
# the writing end of that pipe):
exec 3>&-

Especialmente em scripts, como alternativa, você pode redirecionar um grupo de comandos inteiro em vez de abrir e fechar manualmente o fd com exec³.

{
  echo ... >&3
  ...
  ...
} 3> p1

¹ que fd 3 será herdado por processos filhos e (exceto em ksh93 que define o sinalizador close-on-exec nele) por outros comandos executados nesses processos, se houver.

² que também funciona para comandos não integrados, é claro. Para não integrados, o shell não precisa salvar e restaurar fd 1, pois o redirecionamento é executado apenas no processo filho que é bifurcado para executar o comando. Para comandos externos, exceto no ksh93 que faz isso automaticamente, você pode querer fechar o fd 3 para que não vaze para eles e para processos em segundo plano que eles possam eventualmente gerar ( cmd >&3 3>&-).

³ observe que, nesse caso, ksh93 não define o sinalizador close-on-exec nesse fd 3.

Responder2

Você pode usar tail -fpara manter o fifo aberto após echogravá-lo.

tail -n1 -f p1 | python

Por que isso funciona

pythonestá lendo de p1. Quando chega ao final do arquivo, ele para de ler. Este é um comportamento normal para leituras de arquivos, mesmo que o arquivo seja um canal nomeado. tailcom o -fsinalizador (follow) continuará lendo um arquivo após seu final ser alcançado.

Responder3

Você precisa enviar o programa inteiro de uma vez.

Quando você chama run, python < p1o shell aguarda a entrada antes de invocar o python. Ou seja, o python nem começa a executarde forma algumaaté que todo o fluxo de dados tenha sido lido pelo shell e então passado integralmente para python.

Mesmo em execução python -u p1(ou seja, sem buffer e lido de file p1) pythontentará ler o arquivo inteiro antes de executar qualquer um deles.

Experimente esta experiência.

Terminal 1:

mkfifo p1
python < p1

Terminal 2:

cat > p1
print "Hello World"
print "Hello World"

Você verá que pode enviar várias linhas, mas o python no Termo 1 não faz nada. Agora pressione ctrl+ D. Todo o programa é executado de uma só vez.

Então, para resumir, se você quiser que o python leia um pipe, você precisa enviar o programa inteiro. Você não pode usar python interativamente dessa maneira.

Responder4

Aqui está outra solução. Terminal 1:

alec@MacBook-Air ~/hello $ rm -f my.fifo; mkfifo my.fifo 
alec@MacBook-Air ~/hello $ cat my.fifo 
command1
command2
...
alec@MacBook-Air ~/hello $ 

Terminal 2:

alec@MacBook-Air ~/hello $ cat > my.fifo 
command1
command2
...
alec@MacBook-Air ~/hello $ 

Você digita seus comandos no Terminal 2 e os observa aparecendo no Terminal 1. Você pode canalizar a saída do Terminal 1 para outro processo, por exemplo cat my.fifo | node .... Quando terminar, feche stdino Terminal 2 com Ctrl-D. Isso fechará my.fifoe o catcomando no Terminal 1 será encerrado.

informação relacionada