Как вы читаете из stdin в python из трубы, которая не имеет конца
У меня проблема с чтением из стандартного ввода или канала в python, когда труба находится из "открытого" (не знаю правильного имени)
файл.
У меня есть пример
pipetest.py:
import sys
import time
k = 0
try:
for line in sys.stdin:
k = k + 1
print line
except KeyboardInterrupt:
sys.stdout.flush()
pass
print k
Я запускаю программу, которая продолжает выводить и Ctrl + c через некоторое время
$ ping 127.0.0.1 | python pipetest.py
^C0
У меня нет вывода.
Но если я иду через обычный файл, он работает.
$ ping 127.0.0.1 > testfile.txt
это заканчивается Ctrl + c через короткое время
$ cat testfile.txt | python pipetest.py
PING 127.0.0.1 (127.0.0.1) 56(84) bytes of data.
64 bytes from 127.0.0.1: icmp_seq=1 ttl=64 time=0.017 ms
64 bytes from 127.0.0.1: icmp_seq=2 ttl=64 time=0.015 ms
64 bytes from 127.0.0.1: icmp_seq=3 ttl=64 time=0.014 ms
64 bytes from 127.0.0.1: icmp_seq=4 ttl=64 time=0.013 ms
64 bytes from 127.0.0.1: icmp_seq=5 ttl=64 time=0.012 ms
--- 127.0.0.1 ping statistics ---
5 packets transmitted, 5 received, 0% packet loss, time 3998ms
rtt min/avg/max/mdev = 0.012/0.014/0.017/0.003 ms
10
Как сделать, чтобы получить какой-либо вывод до окончания программы, в этом случае ping закончился?
Ответы
Ответ 1
Попробуйте следующее:
import sys
import time
k = 0
try:
buff = ''
while True:
buff += sys.stdin.read(1)
if buff.endswith('\n'):
print buff[:-1]
buff = ''
k = k + 1
except KeyboardInterrupt:
sys.stdout.flush()
pass
print k
Ответ 2
k = 0
try:
while True:
print sys.stdin.readline()
k += 1
except KeyboardInterrupt:
sys.stdout.flush()
pass
print k
Ответ 3
в то время как sys.stdin является файлоподобным объектом, что означает, что вы можете перебирать его строки, он будет блокироваться до тех пор, пока не будет вставлен EOF.
Поведение может быть описано с помощью следующего псевдокода:
while True:
input = ""
c = stdin.read(1)
while c is not EOF:
input += c
c = stdin.read(1)
for line in input.split('\n'):
yield line
это означает, что, хотя вы можете перебирать строки sys.stdin, вы не можете использовать этот подход для задачи под рукой, и вы должны явно вызвать read() или readline()
Ответ 4
Вот как я это сделал. Мне не нравилось какое-либо другое решение, они не казались очень питоническими.
Это создаст контейнер для ввода любого открытого файла для итерации по всем строкам. Это также позаботится о закрытии файла в конце диспетчера контекстов.
Мне кажется, что это, вероятно, как блок for line in sys.stdin:
должен работать по умолчанию.
class FileInput(object):
def __init__(self, file):
self.file = file
def __enter__(self):
return self
def __exit__(self, *args, **kwargs):
self.file.close()
def __iter__(self):
return self
def next(self):
line = self.file.readline()
if line == None or line == "":
raise StopIteration
return line
with FileInput(sys.stdin) as f:
for line in f:
print f
with FileInput(open('tmpfile') as f:
for line in f:
print f
В командной строке должны работать оба следующих параметра:
tail -f /var/log/debug.log | python fileinput.py
cat /var/log/debug.log | python fileinput.py
Ответ 5
Чтобы это работало, не дожидаясь окончания потока stdin, вы можете использовать его для чтения. Я думаю, что это самое простое решение.
import sys
k = 0
try:
for line in iter(sys.stdin.readline, b''):
k = k + 1
print line
except KeyboardInterrupt:
sys.stdout.flush()
pass
print k