【问题标题】:Merge and sync stdout and stderr?合并和同步标准输出和标准错误?
【发布时间】:2011-06-26 10:53:28
【问题描述】:

假设我正在使用 python 脚本运行 exe:

subprocess.call(cmdArgs,stdout=outf, stderr=errf)

当 outf 和 errf 是文本文件的文件描述符时。

有什么方法可以在它之上生成一个标准输出和标准错误的合并和同步文本文件? 它应该使用时间和来源(我们/错误)进行格式化。

谢谢

【问题讨论】:

标签: python subprocess stdout stderr


【解决方案1】:

这有点棘手,因为您需要在子进程运行时轮询它的 stdout 和 stderr 文件描述符,以获得准确的时间戳。您还需要将输出拆分为行列表,以便可以轻松地合并和排序最终结果。您可以在读取两个流时轻松合并它们,但这不是问题的一部分。

我写得很快,但它可以变得更简洁更紧凑:

import datetime
import os
import select
import subprocess

class Stream(object):

    def __init__(self, name, impl):
        self._name = name
        self._impl = impl
        self._buf = ''
        self._rows = []

    def fileno(self):
        "Pass-through for file descriptor."
        return self._impl.fileno()

    def read(self, drain=0):
        "Read from the file descriptor. If 'drain' set, read until EOF."
        while self._read() is not None:
            if not drain:
                break

    def _read(self):
        "Read from the file descriptor"
        fd = self.fileno()
        buf = os.read(fd, 4096)
        if not buf:
            return None
        if '\n' not in buf:
            self._buf += buf
            return []

        # prepend any data previously read, then split into lines and format
        buf = self._buf + buf
        tmp, rest = buf.rsplit('\n', 1)
        self._buf = rest
        now = datetime.datetime.now().isoformat()
        rows = tmp.split('\n')
        self._rows += [(now, '%s %s: %s' % (self._name, now, r)) for r in rows]

def run(cmd, timeout=0.1):
    """
    Run a command, read stdout and stderr, prefix with timestamp, and
    return a dict containing stdout, stderr and merged.
    """
    PIPE = subprocess.PIPE
    proc = subprocess.Popen(cmd, stdout=PIPE, stderr=PIPE)
    streams = [
        Stream('stdout', proc.stdout),
        Stream('stderr', proc.stderr)
        ]
    def _process(drain=0):
        res = select.select(streams, [], [], timeout)
        for stream in res[0]:
            stream.read(drain)

    while proc.returncode is None:
        proc.poll()
        _process()
    _process(drain=1)

    # collect results, merge and return
    result = {}
    temp = []
    for stream in streams:
        rows = stream._rows
        temp += rows
        result[stream._name] = [r[1] for r in rows]
    temp.sort()
    result['merged'] = [r[1] for r in temp]
    return result

res = run(['ls', '-l', '.', 'xyzabc'])
for key in ('stdout', 'stderr', 'merged'):
    print 
    print '\n'.join(res[key])
    print '-'*40

示例输出:

stdout 2011-03-03T19:30:44.838145: .:
stdout 2011-03-03T19:30:44.838145: total 0
stdout 2011-03-03T19:30:44.838338: -rw-r--r-- 1 pat pat 0 2011-03-03 19:30 bar
stdout 2011-03-03T19:30:44.838518: -rw-r--r-- 1 pat pat 0 2011-03-03 19:30 foo
----------------------------------------

stderr 2011-03-03T19:30:44.837189: ls: cannot access xyzabc: No such file or directory
----------------------------------------

stderr 2011-03-03T19:30:44.837189: ls: cannot access xyzabc: No such file or directory
stdout 2011-03-03T19:30:44.838145: .:
stdout 2011-03-03T19:30:44.838145: total 0
stdout 2011-03-03T19:30:44.838338: -rw-r--r-- 1 pat pat 0 2011-03-03 19:30 bar
stdout 2011-03-03T19:30:44.838518: -rw-r--r-- 1 pat pat 0 2011-03-03 19:30 foo
----------------------------------------

【讨论】:

  • 要正确合并,除了时间戳之外,还需要一个序列号。原因是,当将多行打包到缓冲区中时,它们都以相同的时间戳结束。但是,Python 排序是稳定的,因此如果您将排序键限制为仅时间戳,则无需引入序列号。
【解决方案2】:

您可以合并它们,将subprocess.STDOUT 作为subprocess.Popenstderr 参数传递,但我不知道它们是否会根据时间和来源进行格式化。

【讨论】:

  • 我希望它是除了 stdout.txt 和 stderr.txt 文件之外的,即 3 个输出:1.stdout、2.stderr、3.merge
猜你喜欢
  • 2011-09-01
  • 2012-12-11
  • 2011-02-23
  • 1970-01-01
  • 2014-07-22
  • 1970-01-01
  • 2016-01-10
  • 2014-08-25
相关资源
最近更新 更多