简体   繁体   English

Python:subprocess32 process.stdout.readline()等待时间

[英]Python: subprocess32 process.stdout.readline() waiting time

If I run the following function "run" with for example "ls -Rlah /" I get output immediately via the print statement as expected 如果我使用例如“ls -Rlah /”运行以下函数“run”,我会立即通过print语句获得输出

import subprocess32 as subprocess
def run(command):
    process = subprocess.Popen(command,
                               stdout=subprocess.PIPE,
                               stderr=subprocess.STDOUT)
    try:
        while process.poll() == None:
            print process.stdout.readline()
    finally:
        # Handle the scenario if the parent
        # process has terminated before this subprocess
        if process.poll():
            process.kill()

However if I use the python example program below it seems to be stuck on either process.poll() or process.stdout.readline() until the program has finished. 但是,如果我使用下面的python示例程序,它似乎停留在process.poll()或process.stdout.readline()上,直到程序完成。 I think it is stdout.readline() since if I increase the number of strings to output from 10 to 10000 (in the example program) or add in a sys.stdout.flush() just after every print, the print in the run function does get executed. 我认为它是stdout.readline(),因为如果我将输出的字符串数量从10增加到10000(在示例程序中)或者在每次打印后添加到sys.stdout.flush()中,则在运行中打印函数确实被执行了。

How can I make the output from a subprocess more real-timeish? 如何使子进程的输出更加实时?

Note: I have just discovered that the python example program does not perform a sys.stdout.flush() when it outputs, is there a way for the caller of subprocess to enforce this somehow? 注意:我刚刚发现python示例程序在输出时不执行sys.stdout.flush(),是否有一种方法让子进程的调用者以某种方式强制执行此操作?

Example program which outputs 10 strings every 5 seconds. 每5秒输出10个字符串的示例程序。

#!/bin/env python
import time

if __name__ == "__main__":

    i = 0
    start = time.time()
    while True:
        if time.time() - start >= 5:
            for _ in range(10):
                print "hello world" + str(i)
            start = time.time()
            i += 1
        if i >= 3:
            break

On most systems, command line programs line buffer or block buffer depending on whether stdout is a terminal or a pipe. 在大多数系统上,命令行程序行缓冲区或块缓冲区,具体取决于stdout是终端还是管道。 On unixy systems, the parent process can create a pseudo-terminal to get terminal-like behavior even though the child isn't really run from a terminal. 在unixy系统上,即使子节点并非真正从终端运行,父进程也可以创建一个伪终端来获得类似终端的行为。 You can use the pty module to create a pseudo-terminal or use the pexpect module which eases access to interactive programs. 您可以使用pty模块创建伪终端或使用pexpect模块,以便轻松访问交互式程序。

As mentioned in comments, using poll to read lines can result in lost data. 正如评论中所提到的,使用poll来读取行会导致数据丢失。 One example is data left in the stdout pipe when the process terminates. 一个例子是当进程终止时在stdout管道中留下的数据。 Reading pty is a bit different than pipes and you'll find you need to catch an IOError when the child closes to get it all to work properly as in the example below. 读取pty与管道有点不同,你会发现当孩子关闭时需要捕获一个IOError,以使其全部正常工作,如下例所示。

try:
    import subprocess32 as subprocess
except ImportError:
    import subprocess
import pty
import sys
import os
import time
import errno

print("running %s" % sys.argv[1])

m,s = (os.fdopen(pipe) for pipe in pty.openpty())
process = subprocess.Popen([sys.argv[1]],
                           stdin=s,
                           stdout=s,
                           stderr=subprocess.STDOUT)
s.close()

try:
    graceful = False
    while True:
        line = m.readline()
        print line.rstrip()
except IOError, e:
    if e.errno != errno.EIO:
        raise
    graceful = True
finally:
    # Handle the scenario if the parent
    # process has terminated before this subprocess
    m.close()
    if not graceful:
        process.kill()
    process.wait()

You should flush standard output in your script: 您应该在脚本中刷新标准输出:

print "hello world" + str(i)
sys.stdout.flush()

When standard output is a terminal, stdout is line-buffered. 当标准输出是终端时,stdout是行缓冲的。 But when it is not, stdout is block buffered and you need to flush it explicitly. 但是当它不是时,stdout是块缓冲的,你需要显式刷新它。

If you can't change the source of your script, you can use the -u option of Python (in the subprocess): 如果无法更改脚本的源代码,可以使用Python的-u选项(在子进程中):

-u     Force stdin, stdout and stderr to be totally unbuffered. 

Your command should be: ['python', '-u', 'script.py'] 你的命令应该是: ['python', '-u', 'script.py']

In general, this kind of buffering happens in userspace. 通常,这种缓冲发生在用户空间中。 There are no generic ways to force an application to flush its buffers: some applications support command line options (like Python), others support signals, others do not support anything. 没有通用的方法来强制应用程序刷新其缓冲区:某些应用程序支持命令行选项(如Python),其他应用程序支持信号,其他应用程序不支持任何内容。

One solution might be to emulate a pseudo terminal, giving "hints" to the programs that they should operate in line-buffered mode. 一种解决方案可能是模拟伪终端,给程序提供“提示”,它们应该在行缓冲模式下运行。 Still, this is not a solution that works in every case. 尽管如此,这并不是一个适用于所有情况的解决方案。

For things other than python you could try using unbuffer : 对于除python以外的东西,你可以尝试使用unbuffer

unbuffer disables the output buffering that occurs when program output is redirected from non-interactive programs. unbuffer禁用从非交互式程序重定向程序输出时发生的输出缓冲。 For example, suppose you are watching the output from a fifo by running it through od and then more. 例如,假设您通过运行od然后更多来查看fifo的输出。 od -c /tmp/fifo | od -c / tmp / fifo | more You will not see anything until a full page of output has been produced. 更多在产生整页输出之前,您不会看到任何内容。 You can disable this automatic buffering as follows: 您可以按如下方式禁用此自动缓冲:

unbuffer od -c /tmp/fifo | more

Normally, unbuffer does not read from stdin. 通常,unbuffer不会从stdin读取。 This simplifies use of unbuffer in some situations. 这在某些情况下简化了unbuffer的使用。 To use unbuffer in a pipeline, use the -p flag. 要在管道中使用unbuffer,请使用-p标志。 Example: process1 | 示例:process1 | unbuffer -p process2 | unbuffer -p process2 | process3 process3

So in your case: 所以在你的情况下:

run(["unbuffer",cmd]) 

There are some caveats listed in the docs but it is another option. 文档中列出了一些警告,但这是另一种选择。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM