непосредственный трубопровод с питоном - PullRequest
5 голосов
/ 18 марта 2012

У меня есть два файла, первым из которых является fizz

#!/usr/bin/python                                                         
import time

print 'started'
time.sleep(3)
print 'ended'

следующий из которых - бар

#!/usr/bin/python                                                         
import sys

for line in sys.stdin:
    print line

Когда я запускаю команду ./fizz | ./bar Я ожидаю, что она напечатает started, затем подождите 3 секунды и напечатает ended, но на самом деле происходит то, что она печатает started и ended одновременно после 3 секунд. Есть ли способ получить желаемое поведение? Спасибо

Ответы [ 3 ]

6 голосов
/ 18 марта 2012

Теперь, когда стало ясно, что проблема на стороне получателя, я представляю альтернативу, которую мне нравится использовать:

#!/usr/bin/python                                                   
import sys 
import os

for line in iter(sys.stdin.readline, ''):
    sys.stdout.write(line) # \n included in line

iter(func, sentinel) вызывает func() для каждой итерации и завершается, если результат функции == sentinel.

5 голосов
/ 18 марта 2012

Хороший вопрос. Это немного сложнее, чем следовало.

Проблема действительно в bar, в частности sys.stdin в буфере. Я попытался открыть sys.stdin с меньшим размером буфера и использовать python -u, но это не сработало. На man-странице сказано следующее:

   -u     Force  stdin,  stdout  and  stderr to be totally unbuffered.  On
          systems where it matters, also put stdin, stdout and  stderr  in
          binary  mode.   Note  that there is internal buffering in xread‐
          lines(), readlines() and file-object  iterators  ("for  line  in
          sys.stdin")  which  is  not  influenced by this option.  To work
          around this, you will want to use "sys.stdin.readline()"  inside
          a "while 1:" loop.

В конце концов это то, что у меня сработало:

#!/usr/bin/python                                                   
import sys 
import os

while True:
    line = sys.stdin.readline()
    if not line:
        break
    sys.stdout.write(line) # or print, doesn't matter.
1 голос
/ 18 марта 2012

Есть две проблемы:

  1. print "something" в ./foo не очищает свой буфер stdout, если он перенаправлен (в данном случае на канал), т. Е. Когда stdout не подключен к tty-подобному устройству , например, к интерактивная консоль
  2. for line in sys.stdin: может попытаться прочитать несколько строк одновременно

Вы можете исправить это следующим образом:

$ PYTHONUNBUFFERED=1 ./foo | ./bar

Где ./bar:

#!/usr/bin/python
import sys

for line in iter(sys.stdin.readline, ''):
    print line,

то есть, сделать стандартный вывод ./foo небуферизованным (-u опция ) и построчно читать входные данные в ./bar, как предложено в @ ответ Эдуардо Иванека .

В качестве альтернативы вы можете вызвать sys.stdout.flush() в ./foo вместо того, чтобы сделать его стандартный буфер небуферизованным, как предложено в @ kev's answer .

...