Bash管道到python

Sri*_*yer 6 python linux bash shell

我需要通过管道实时吸收bash命令的输出.例如

for i in $(seq 1 4); do echo $i; sleep 1; done | ./script.py
Run Code Online (Sandbox Code Playgroud)

script.py有这个

for line in sys.stdin.readlines():
        print line
Run Code Online (Sandbox Code Playgroud)

我希望序列在可用时打印,但python脚本在继续之前等待bash脚本结束.

我看了这个相关的答案,但这并没有解决我的问题.我如何在python中实现这一目标?

aba*_*ert 8

第一个问题是readlines将所有行读入列表.直到所有线都存在才能做到这一点,直到stdin达到EOF 才会这样做.

但是你实际上并不需要一个行列表,只需要一些可迭代的行.像一个文件sys.stdin已经一个可迭代的文件.它是一个懒惰的,一旦它们可用就一次生成一行,而不是等待一次生成它们.

所以:

for line in sys.stdin:
    print line
Run Code Online (Sandbox Code Playgroud)

每当你发现自己伸手可及的时候readlines,问问自己是否真的需要它.答案永远是否定的.(好吧,除非你想用一个参数调用它,或者在一些有缺陷的不完全文件的对象上调用它.)参见Readlines Considered Silly for more.


但同时,还有第二个问题.不是Python正在缓冲它stdin,或者其他进程正在缓冲它stdout,但是文件对象迭代器本身正在进行内部缓冲,这可能(取决于你的平台 - 但在大多数POSIX平台上,它通常会)阻止你从第一行到EOF,或者至少直到阅读了很多行.

这是一个已知的问题与Python 2.x中,它已被固定在3.x中,*但这并不能帮助你,除非你愿意升级.

在命令行和环境文档中以及大多数系统的联机帮助页中都提到了该解决方案,但这些解决方案位于-u标记文档的中间:

请注意,xreadlines(),readlines()和file-object迭代器("for sys.stdin中的行")中存在内部缓冲,不受此选项的影响.要解决此问题,您需要在"while 1:"循环中使用"sys.stdin.readline()".

换一种说法:

while True:
    line = sys.stdin.readline()
    if not line:
        break
    print line
Run Code Online (Sandbox Code Playgroud)

要么:

for line in iter(sys.stdin.readline, ''):
    print line
Run Code Online (Sandbox Code Playgroud)

对于另一个问题,在这个答案中,Alex Martelli指出你总是可以忽略sys.stdin并重新编写fdopen文件描述符.这意味着你得到一个POSIX fd而不是C stdio句柄的包装器.但这对于这个问题来说既不必要也不充分,因为问题不在于C stdio缓冲,而在于file.__iter__缓冲与它相互作用的方式.


*Python 3.x不再使用C stdio库的缓冲了; 它在io模块中的类型中完成所有操作,这意味着迭代器可以共享文件对象本身使用的相同缓冲区.虽然io在2.x上也可以使用,但它不是openstdio文件句柄的默认设置,这就是为什么它在这里没有帮助.

  • 至少在OS X上,在Python有任何输出之前,仍然必须等待整个循环 (3认同)

daw*_*awg 6

使用Python 2.7.9(可能是3.x之前的所有Python),这可以达到您的期望:

#!/usr/bin/python

import sys

while True:
   line=sys.stdin.readline()
   if not line:
      break
   print line   
Run Code Online (Sandbox Code Playgroud)

你也可以这样做:

#!/usr/bin/python

import sys

for line in iter(sys.stdin.readline, ''):
   print line 
Run Code Online (Sandbox Code Playgroud)

在Python 3.4.3上,您可以执行abarnert建议的操作:

#!/usr/local/bin/python3

import sys

for line in sys.stdin:
    print(line)
Run Code Online (Sandbox Code Playgroud)

您也可以使用Python 3使用的io类重新打开sys.stdin :

#!/usr/bin/python

import sys, io

for line in io.open(sys.stdin.fileno()):
    print(line)
Run Code Online (Sandbox Code Playgroud)

第一,第二和最后一个方法都适用于OS X上的Python 2.7.6和2.7.9以及Python 3.4.3; 第三种方法,仅适用于Python 3.