首页 文章

你能像往常一样制作python子进程输出stdout和stderr,还能将输出捕获为字符串吗? [重复]

提问于
浏览
26

可能重复:包装子进程'stdout / stderr

this question中,hanan-n询问是否可以将python子进程输出到stdout,同时还将输出保存在字符串中以供以后处理 . 在这种情况下,解决方案是遍历每个输出行并手动打印它们:

output = []
p = subprocess.Popen(["the", "command"], stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, ''):
    print(line)
    output.append(line)

但是,此解决方案并未概括为您要为stdout和stderr执行此操作的情况,同时满足以下条件:

  • stdout / stderr的输出应分别转到父进程'stdout / stderr

  • 输出应该尽可能实时完成(但我只需要访问最后的字符串)

  • 不应该更改stdout和stderr行之间的顺序(我现在假设我们得到包含完整行的好块中的所有内容?)

我查看了subprocess documentation,但找不到任何可以实现此目的的东西 . 我能找到的最接近的是添加 stderr=subprocess.stdout 并使用与上面相同的解决方案,但是我们失去了常规输出和错误之间的区别 . 有任何想法吗?我猜测解决方案 - 如果有的话 - 将涉及到 p.stdoutp.stderr 的异步读取 .

这是我想做的一个例子:

p = subprocess.Popen(["the", "command"])
p.wait()  # while p runs, the command's stdout and stderr should behave as usual
p_stdout = p.stdout.read()  # unfortunately, this will return '' unless you use subprocess.PIPE
p_stderr = p.stderr.read()  # ditto
[do something with p_stdout and p_stderr]

3 回答

  • 36

    这个例子似乎对我有用:

    # -*- Mode: Python -*-
    # vi:si:et:sw=4:sts=4:ts=4
    
    import subprocess
    import sys
    import select
    
    p = subprocess.Popen(["find", "/proc"],
        stdout=subprocess.PIPE, stderr=subprocess.PIPE)
    
    stdout = []
    stderr = []
    
    while True:
        reads = [p.stdout.fileno(), p.stderr.fileno()]
        ret = select.select(reads, [], [])
    
        for fd in ret[0]:
            if fd == p.stdout.fileno():
                read = p.stdout.readline()
                sys.stdout.write('stdout: ' + read)
                stdout.append(read)
            if fd == p.stderr.fileno():
                read = p.stderr.readline()
                sys.stderr.write('stderr: ' + read)
                stderr.append(read)
    
        if p.poll() != None:
            break
    
    print 'program ended'
    
    print 'stdout:', "".join(stdout)
    print 'stderr:', "".join(stderr)
    

    一般情况下,如果你想同时使用多个文件描述符并且你不知道哪个会有东西供你阅读,你应该使用select或类似的东西(如Twisted reactor) .

  • 9

    要以可移植的方式打印到控制台并在子进程的字符串stdout / stderr中捕获:

    from StringIO import StringIO
    
    fout, ferr = StringIO(), StringIO()
    exitcode = teed_call(["the", "command"], stdout=fout, stderr=ferr)
    stdout = fout.getvalue()
    stderr = ferr.getvalue()
    

    其中 teed_call()Python subprocess get children's output to file and terminal?中定义

    您可以使用任何类似文件的对象( .write() 方法) .

  • 2

    如上所述创建两个读者,一个用于 stdout 一个用于 stderr ,并在新线程中启动每个读取器 . 这将以与流程输出的顺序大致相同的顺序附加到列表中 . 如果需要,保留两个单独的列表 .

    即,

    p = subprocess.Popen(["the", "command"])
    t1 = thread.start_new_thread(func,stdout)  # create a function with the readers
    t2 = thread.start_new_thread(func,stderr)
    p.wait() 
    # your logic here
    

相关问题