合并Python脚本的子进程'stdout和stderr,同时保持它们可区分

bee*_*tee 32 python subprocess stdout stderr

我想将python脚本的子进程'stdout和stdin指向同一个文件.我不知道的是如何使两个来源的线条区分开来?(例如,带有感叹号的stderr行前缀.)

在我的特定情况下,不需要对子进程进行实时监视,执行的Python脚本可以等待其执行结束.

小智 44

tsk = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
Run Code Online (Sandbox Code Playgroud)

subprocess.STDOUT 是一个特殊的标志,告诉subprocess将所有stderr输出路由到stdout,从而组合你的两个流.

顺便说一句,select在Windows中没有poll().subprocess仅使用文件句柄号,而不调用文件输出对象的write方法.

捕获输出,执行以下操作:

logfile = open(logfilename, 'w')

while tsk.poll() is None:
    line = tsk.stdout.readline()
    logfile.write(line)
Run Code Online (Sandbox Code Playgroud)

  • 仍然+1,因为这是一个有用的事情来了解这个主题 (7认同)
  • 你可能错过了"如何使两个来源的线条可以区分"问题的一部分.`subprocess.STDOUT`没有办法区分stdout/err. (6认同)

小智 12

我发现自己最近不得不解决这个问题,并且在大多数情况下需要一段时间才能得到我认为正常工作的东西,所以这就是它!(它还具有通过python记录器处理输出的良好副作用,我注意到这是Stackoverflow上的另一个常见问题).

这是代码:

import sys
import logging
import subprocess
from threading import Thread

logging.basicConfig(stream=sys.stdout,level=logging.INFO)
logging.addLevelName(logging.INFO+2,'STDERR')
logging.addLevelName(logging.INFO+1,'STDOUT')
logger = logging.getLogger('root')

pobj = subprocess.Popen(['python','-c','print 42;bargle'], 
    stdout=subprocess.PIPE, stderr=subprocess.PIPE)

def logstream(stream,loggercb):
    while True:
        out = stream.readline()
        if out:
            loggercb(out.rstrip())       
        else:
            break

stdout_thread = Thread(target=logstream,
    args=(pobj.stdout,lambda s: logger.log(logging.INFO+1,s)))

stderr_thread = Thread(target=logstream,
    args=(pobj.stderr,lambda s: logger.log(logging.INFO+2,s)))

stdout_thread.start()
stderr_thread.start()

while stdout_thread.isAlive() and stderr_thread.isAlive():
     pass
Run Code Online (Sandbox Code Playgroud)

这是输出:

STDOUT:root:42
STDERR:root:Traceback (most recent call last):
STDERR:root:  File "<string>", line 1, in <module>
STDERR:root:NameError: name 'bargle' is not defined
Run Code Online (Sandbox Code Playgroud)

您可以替换子进程调用以执行您想要的任何操作,我只选择使用我知道将打印到stdout和stderr的命令运行python.关键位是在一个单独的线程中读取stderr和stdout.否则,当有数据准备好在另一个上读取时,您可能会阻止读取一个.

  • 与Rojan的答案不同,这适用于Windows,这很好.虽然我认为`而stdout_thread.isAlive()和stderr_thread.isAlive()`是低效的,可以用`stdout_thread.join()``stderr_thread.join()代替` (3认同)

T.R*_*jan 9

如果要交错以获得与以交互方式运行进程时大致相同的顺序,则需要执行shell所执行的操作并轮询stdin/stdout并按其轮询的顺序进行写入.

这里有一些代码可以按照您的需要执行某些操作 - 在这种情况下,将stdout/stderr发送到记录器信息/错误流.

tsk = subprocess.Popen(args,stdout=subprocess.PIPE,stderr=subprocess.PIPE)

poll = select.poll()
poll.register(tsk.stdout,select.POLLIN | select.POLLHUP)
poll.register(tsk.stderr,select.POLLIN | select.POLLHUP)
pollc = 2

events = poll.poll()
while pollc > 0 and len(events) > 0:
  for event in events:
    (rfd,event) = event
    if event & select.POLLIN:
      if rfd == tsk.stdout.fileno():
        line = tsk.stdout.readline()
        if len(line) > 0:
          logger.info(line[:-1])
      if rfd == tsk.stderr.fileno():
        line = tsk.stderr.readline()
        if len(line) > 0:
          logger.error(line[:-1])
    if event & select.POLLHUP:
      poll.unregister(rfd)
      pollc = pollc - 1
    if pollc > 0: events = poll.poll()
tsk.wait()
Run Code Online (Sandbox Code Playgroud)


Cin*_*quo 0

命令执行后,您可以将 stdout/err 写入文件。在下面的示例中,我使用 pickling,因此我确信我能够在不进行任何特定解析的情况下进行读取,以区分 stdout/err,并且在某些时候我可以复制退出代码和命令本身。

import subprocess
import cPickle

command = 'ls -altrh'
outfile = 'log.errout'
pipe = subprocess.Popen(command, stdout = subprocess.PIPE,
                        stderr = subprocess.PIPE, shell = True)
stdout, stderr = pipe.communicate()

f = open(outfile, 'w')
cPickle.dump({'out': stdout, 'err': stderr},f)
f.close()
Run Code Online (Sandbox Code Playgroud)

  • OP 想要 stdout/err *交错*。 (5认同)