【问题标题】:Calling python script with subprocess.Popen and flushing the data使用 subprocess.Popen 调用 python 脚本并刷新数据
【发布时间】:2013-01-05 21:03:27
【问题描述】:

好吧,我已经看到了十几个这样的线程,但没有一个给出完整的答案,而且到目前为止我尝试的一切都对我不起作用。

1) 不断输出一些数据并刷新它的脚本:

import time
import sys

if __name__ == '__main__':
    for i in range(5):
        print i,
        sys.stdout.flush()
        time.sleep(1)

2) 使用 Popen 调用第一个脚本的脚本应该一个一个地打印数字,但由于某种原因没有,并且一次打印它们:

import sys
import subprocess

if __name__ == '__main__':
    process = subprocess.Popen(['python', 'flush.py'], stdout = subprocess.PIPE )
    for line in iter(process.stdout.readline, ''):
        print line,
        sys.stdout.flush()

我有点困惑的第一件事是在 first 脚本中,如果你删除刷新,它会在一行中返回输出 O_O...我很确定这是因为时间.sleep 但仍然有点期望它像标准输出一样返回,不断返回值 0,1,2,3,4 但不是全部一起,当然 flush 解决了它,但只是奇怪,至少对我来说......

主要问题: 是不是第二个脚本不会一一返回数字,而是一次返回所有输出......我需要的是看到数字一一弹出......

我在某处读到它不返回 Popen 等待关闭管道的 EOF,这就是它运行到最后的原因.....

那么接下来我该怎么做或尝试呢? 提前致谢。

【问题讨论】:

  • 可能是因为您的子进程没有在每个数字后打印换行符。 readline 一直读取到换行符(或文件结尾)。看看我的回答是否有帮助:stackoverflow.com/questions/12255802/…
  • 嗯,基本上我找到了我的问题的解决方案。这是有效的代码,尽管我并不真正掌握它背后的理论link。为什么你要在线程中执行它,在某个地方我读到不是阻塞主线程,但是它又是如何帮助的,如果你仍然只在线程中将整个文本放在一起,那对我来说毫无意义,因为我不明白它是怎么回事我猜是有效的。然后将文件描述符设置为非阻塞的事情也让我很困惑。
  • @Viktor: the link that you provided 是不必要的复杂。它之所以有效,是因为它不使用readline()(无论如何它都不适用于非阻塞管道),因此它不会等待您的子脚本永远不会产生的换行符。这是simpler solution that should be enough for line-oriented output in many cases

标签: python subprocess pipe


【解决方案1】:

正如@Warren Weckesser's comment 所说,您的问题与buffering issues 无关。

父进程中的.readline() 在读取换行符或到达EOF 之前不会返回。您的子进程根本不会打印 any 换行符,因此您的父进程在子进程结束之前不会打印 anything

最小的解决方法是删除子脚本中print i, 末尾的逗号。

这也有效:

#!/usr/bin/env python
import sys
from subprocess import Popen, PIPE

p = Popen([sys.executable or 'python',
           '-u', # unbuffer stdout (or make it line-buffered on Python 3)
           '-c',
           """
import time

for i in range(5):
    print(i) # <-- no comma i.e., each number is on its own line
    time.sleep(1)
"""], stdout=PIPE, bufsize=1)
for line in iter(p.stdout.readline, b''):
    print(int(line)**2)

例子:

 $ python parent.py
 0
 1
 4
 9
 16

数字每秒钟打印一次,无需等待子进程结束。

如果您不想更改子脚本,那么您应该使用在空格处停止的readline(),而不是换行符,例如:

#!/usr/bin/env python
import sys
from subprocess import Popen, PIPE

p = Popen(['python2', 'child.py'], stdout=PIPE, bufsize=0)
for token in generate_tokens(p.stdout):
    print(int(token))

generate_tokens() 产生空格分隔的标记:

def generate_tokens(pipe):
    buf = []
    while True:
        b = pipe.read(1) # read one byte
        if not b: # EOF
            pipe.close()
            if buf:
                yield b''.join(buf)
            return
        elif not b.isspace(): # grow token
            buf.append(b)
        elif buf: # full token read
            yield b''.join(buf)
            buf = []

它也会在孩子打印出整数后立即打印出来。

【讨论】:

    猜你喜欢
    • 2012-04-06
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2016-09-20
    • 1970-01-01
    • 1970-01-01
    • 2014-01-15
    • 1970-01-01
    相关资源
    最近更新 更多