使用Python处理来自子进程的stderr和stdout消息

6
我的Python代码派生了子进程,并且它会在标准输出(stdout)和标准错误(stderr)中打印出消息。我需要分别打印它们。
以下是我的代码,用于生成子进程并从中获取标准输出结果:
cmd = ["vsmake.exe", "-f"]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE)
for line in iter(p.stdout.readline, ''):
    print line,
    sys.stdout.flush()
    pass
p.wait()

如何修改代码以检查子进程是否通过stderr打印消息?

添加

我需要在子进程打印出内容时立即打印出stderr和stdout。并且这是跨平台的实现,因此它应该可以在Mac / Linux / PC上运行。


您是否想要实时在stdout和stderr上打印两个消息?如果是,您可能需要在Windows上使用线程(因为既没有select()也没有非阻塞I/O)。 - Sven Marnach
我编辑了我的回答,以便在任意两个管道中的任何一个输出行时,立即打印标准错误和标准输出。 - Manuel Salvadores
2个回答

7
p = Popen(cmd, bufsize=1024,
stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)
p.stdin.close()
print p.stdout.read() #This will print the standard output from the spawned process
print p.stderr.read() #This is what you need, error output <-----

基本上错误输出被重定向到 stderr 管道。

如果你需要实时获取更多的信息,也就是说当被创建的进程在 stdout 或者 stderr 上打印一些内容时,你可以这样做:

def print_pipe(type_pipe,pipe):
    for line in iter(pipe.readline, ''):
         print "[%s] %s"%(type_pipe,line),

p = Popen(cmd, bufsize=1024,
stdin=PIPE, stdout=PIPE, stderr=PIPE, close_fds=True)

t1 = Thread(target=print_pipe, args=("stdout",p.stdout,))
t1.start()
t2 = Thread(target=print_pipe, args=("stderr",p.stderr,))
t2.start()

#optionally you can join the threads to wait till p is done. This is avoidable but it 
# really depends on the application.
t1.join()
t2.join()

在这种情况下,每当有一行文本被写入stdoutstderr时,两个线程都会打印。参数type_pipe仅用于区分输出的文本是来自stderr还是stdout


3
我最喜欢的(针对小输出)是:stdin,stdout = p.communicate(),一步完成所有操作!然后你可以对stdin和stdout进行任何你想要的处理。 - MikeyB
不错啊!(+1) 我之前不知道这个。 - Manuel Salvadores
@Sven:我在我的问题中添加了更多内容。 - prosseek
1
你应该修复你的 print 语句 -- 目前的 % 运算符会失败。同时记得 line 以 "\n" 结尾,你不想打印两次。 - Sven Marnach
1
  1. 第一个例子如果输出足够大,则可能会阻塞。https://dev59.com/HHVC5IYBdhLWcg3w2lGI#165662
  2. 除非必须,否则不要使用shell=True。与列表cmd的语义不同。
  3. 您可以在print语句末尾添加,,而不是使用line.replace()
  4. 应该在某个地方加上p.wait()t1.join()t2.join()
- jfs
显示剩余3条评论

1

最简单的跨平台方法是使用线程(不幸的是)。以下是一些示例代码:

def redirect_to_stdout(stream):
    for line in stream:
        sys.stdout.write(line)
        sys.stdout.flush()

cmd = ["vsmake.exe", "-f"]
p = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
stderr_thread = threading.Thread(target=redirect_to_stdout, args=(p.stderr,))
stderr_thread.start()
redirect_to_stdout(p.stdout)
p.wait()
stderr_thread.join()

-1:将“run”替换为“start”。使用stderr=subprocess.STDOUT重定向stdout - jfs
@J.F. Sebastian,感谢您指出start()/run()的混淆问题,已经纠正。使用stderr=subprocess.STDOUT无法满足OP的“以不同方式打印[stdout和stderr]”的要求。如果可以使用这种方式,您根本不需要线程。 - Sven Marnach
1
你的解决方案没有区别地打印stdout和stderr;在这种情况下,它不需要线程。 - jfs

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接