我正在编写一个python脚本,可以通过管道从另一个命令读取输入
batch_job | myparser
我的脚本myparser
处理batch_job
的输出并写入自己的标准输出。我的问题是我想立即看到输出(batch_job的输出是逐行处理的)但似乎有这个臭名昭着的stdin缓冲(据称4KB,我还没有被验证)延迟了一切。
我尝试了以下内容:
os.fdopen(sys.stdin.fileno(), 'r', 0)
-u
:#!/usr/bin/python -u
export PYTHONUNBUFFERED=1
我的python版本是2.4.3 - 我无法升级或安装任何其他程序或软件包。我怎样才能摆脱这些延误?
答案 0 :(得分:1)
在Linux bash中,您正在寻找的似乎是stdbuf命令。
如果您不希望缓冲(即无缓冲的流),请尝试此操作
# batch_job | stdbuf -o0 myparser
如果要行缓冲,请尝试此操作
# batch_job | stdbuf -oL myparser
答案 1 :(得分:0)
我在旧版代码中遇到了同样的问题。 Python 2的file
对象的__next__
方法的实现似乎存在问题。它使用Python级别的缓冲区(-u
/ PYTHONUNBUFFERED=1
不会影响,因为那些缓冲区本身仅对stdio
FILE*
进行缓冲,而对file.__next__
缓冲没有关系;类似地,stdbuf
/ unbuffer
根本无法更改任何缓冲,因为Python替换了C运行时创建的默认缓冲;最后file.__init__
对于新打开的文件,调用PyFile_SetBufSize
,它使用setvbuf
/ setbuf
[API]替换默认的stdio
缓冲区)。
当您使用以下形式的循环时,就会看到问题:
for line in sys.stdin:
其中对__next__
的第一次调用(由for
循环隐式调用以获取每个line
)在生成单行之前最终阻塞以填充该块。
有三种可能的修复方法:
(仅在Python 2.6+上)使用sys.stdio
模块(从Python 3作为内置返回源)重新包装io
以完全绕过file
(坦率地说)Python 3设计(一次使用一个系统调用来填充缓冲区,而不会阻塞请求的完整读取的发生;如果它请求4096字节并得到3,它将查看一行是否可用并生成(如果有)):
import io
import sys
# Add buffering=0 argument if you won't always consume stdin completely, so you
# can't lose data in the wrapper's buffer. It'll be slower with buffering=0 though.
with io.open(sys.stdin.fileno(), 'rb', closefd=False) as stdin:
for line in stdin:
# Do stuff with the line
这通常比选项2更快,但是更冗长,并且需要Python 2.6+。通过将模式更改为'r'
并有选择地传递输入的已知encoding
(如果不是语言环境默认值)以无缝获取unicode
,它还允许重新包装对Unicode友好行而不是(仅适用于ASCII)str
。
(任何版本的Python)都可以使用file.__next__
来解决file.readline
的问题;尽管预期行为几乎相同,但是readline
并没有做自己的(过度)缓冲,它委托给C stdio
的{{1}}(默认构建设置)或手动循环调用{{ 1}} / fgets
放入缓冲区,该缓冲区在到达行尾时会完全停止。通过将其与两个参数getc
结合使用,您可以得到几乎相同的代码而不会产生过多的冗长性(它可能比以前的解决方案要慢,这取决于是否在引擎盖下使用getc_unlocked
以及如何使用C运行时将其实现):
iter
移动到没有此问题的Python 3。 :-)
答案 2 :(得分:0)
您可以取消输出缓冲:
unbuffer batch_job | myparser