【问题标题】:Python subprocess output on windows?Windows上的Python子进程输出?
【发布时间】:2012-05-11 12:02:01
【问题描述】:

我在从子进程标准输出管道获取输出时遇到了一些困难。我正在通过它启动一些第三方代码,以提取日志输出。直到最近更新第三方代码,一切正常。更新后,python 开始无限期阻塞,实际上并没有显示任何输出。我可以手动启动第三方应用程序并查看输出。

我正在使用的代码的基本版本:

import subprocess, time
from threading import Thread

def enqueue_output(out):
    print "Hello from enqueue_output"
    for line in iter(out.readline,''):
        line = line.rstrip("\r\n")
        print "Got %s" % line
    out.close()

proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, bufsize=1)
thread = Thread(target=enqueue_output, args=(proc.stdout,))
thread.daemon = True
thread.start()

time.sleep(30)

如果我用 third_party.exe 代替这个脚本,这将完美运行:

import time, sys

while True:
    print "Test"
    sys.stdout.flush()
    time.sleep(1)

所以我不清楚需要做些什么魔法才能让这个与原始命令一起工作。

这些都是 subprocess.Popen 行的变体,我尝试过但没有成功:

proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, bufsize=0)
proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, shell=True)
proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, creationflags=subprocess.CREATE_NEW_CONSOLE)
si = subprocess.STARTUPINFO()
si.dwFlags = subprocess.STARTF_USESTDHANDLES | subprocess.STARTF_USESHOWWINDOW
proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, startupinfo=si)

编辑 1: 在这种情况下,我实际上不能使用 .communicate() 。我正在启动的应用程序会长时间运行(几天到几周)。我可以实际测试 .communicate() 的唯一方法是在应用启动后不久将其终止,我认为这不会给我有效的结果。

即使是非线程版本也失败了:

import subprocess, time
from threading import Thread

proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, stderr=subprocess.PIPE)

print "App started, reading output..."
for line in iter(proc.stdout.readline,''):
    line = line.rstrip("\r\n")
    print "Got: %s" % line

编辑 2: 感谢 jdi,以下工作正常:

import tempfile, time, subprocess

w = "test.txt"
f = open("test.txt","a")
p = subprocess.Popen("third_party.exe", shell=True, stdout=f,
                        stderr=subprocess.STDOUT, bufsize=0)

time.sleep(30)

with open("test.txt", 'r') as r:
    for line in r:
        print line
f.close()

【问题讨论】:

  • 您的第三方程序是否有可能转而写入stderr?
  • 似乎并非如此。如果我拦截 stderr ,同样的事情会发生(虽然,我确实在控制台上看到了应用程序的输出,但它只是打印,它没有通过 Python)。
  • 听起来好像应用程序已停止使用标准输出并直接写入控制台。如果是这样,那么您对此无能为力。您可以与应用的供应商核实吗?
  • 最后一种可能是直接阅读控制台内容,请参阅我对stackoverflow.com/questions/7990851/…的回答
  • 应用程序的供应商通知我他们从 'std::wcout' 切换到 c 样式的标准输出。我不清楚这到底是什么意思。

标签: python windows subprocess


【解决方案1】:

首先,我建议您简化此示例,以确保您可以实际阅读任何内容。从混合中删除线程的复杂性:

proc = subprocess.Popen("third_party.exe", stdout=subprocess.PIPE, bufsize=1)
print proc.communicate()

如果可行,那就太好了。那么您可能在如何直接或可能在您的线程中读取标准输出时遇到问题。

如果这不起作用,您是否尝试过将 stderr 也通过管道传输到 stdout?

proc = subprocess.Popen("third_party.exe", 
                        stdout=subprocess.PIPE, 
                        stderr=subprocess.STDOUT, bufsize=1)

更新

既然你说communicate() 死锁了,你可以试试下面的另一种方法,看看是不是子进程的内部缓冲区有问题...

import tempfile
import subprocess

w = tempfile.NamedTemporaryFile()
p = subprocess.Popen('third_party.exe', shell=True, stdout=w, 
                        stderr=subprocess.STDOUT, bufsize=0)

with open(w.name, 'r') as r:
    for line in r:
        print line
w.close()

【讨论】:

  • 如果我的线程有错误,为什么它适用于我的简单测试应用程序,但不是真实的?我不能使用通信,因为应用程序实际上并没有结束。它是一个服务器,可以长时间运行。
  • @devicenull:我想说不同之处在于您的测试脚本每秒缓慢地打印 4 个字符,而我不知道您的第 3 方应用程序正在执行什么样的输出。您可能会从缓冲区死锁。此外,当您遇到此类问题时,最好尽可能多地删除变量并确保每个步骤都能正常工作。
  • 输出到文件效果很好。有什么想法为什么会起作用,但管道不起作用?
  • @devicenull:这在技术上是一样的,只是使用了一个命名文件。我不记得我以前在哪里见过它,但我读过关于子进程的内部缓冲区达到一定数量的大输出的限制。解决方法是使用您自己的文件描述符。
  • @devicenull:主要区别在于您的edit1 使用stderr=PIPE 而edit2 使用stderr=STDOUT。并且(也许)shell=True 可能会有所作为 - 尝试使用和不使用它。如果您使用stderr=STDOUT,那么您的edit1 可能会起作用——您不需要在这里使用临时文件。
【解决方案2】:
args = ['svn','log','-v']

def foo(info=''):
    import logging
    import subprocess
    import tempfile
    try:
        pipe = subprocess.Popen(args,bufsize = 0,\
            stdout = subprocess.PIPE,\
            stderr=subprocess.STDOUT)
    except Exception as e:
        logging.error(str(e))
        return False
    while 1:
        s = pipe.stdout.read()
        if s:
            print s,
        if pipe.returncode is None:
            pipe.poll()
        else:
            break
    if not 0 == pipe.returncode:
        return False
    return True

print foo()

这个应该可以工作,而不是线程,临时文件魔术。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2018-04-12
    • 2019-08-05
    • 2012-09-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-08-29
    相关资源
    最近更新 更多