使用plumbum捕获前台命令的错误输出

时间:2013-01-08 23:44:54

标签: python plumbum

我正在使用plumbum python库(http://plumbum.readthedocs.org/)替代shell脚本。

我想运行一个命令,当它失败时,它会输出我感兴趣的文件的路径:

$ slow_cmd
Working.... 0%
Working.... 5%
Working... 15%
FAIL. Check log/output.log for details

我想在前台运行程序来检查进度:

from plumbum.cmd import slow_cmd

try:
    f = slow_cmd & FG
except Exception, e:
    print "Something went wrong."

# Need the error output from f to get the log file :(    

slow_cmd失败时,它会抛出异常(我可以捕获)。但我无法从异常或f future对象中获取错误输出。

如果我没有在FG上运行slow_cmd,则异常包含所有输出,我可以从那里读取文件。

1 个答案:

答案 0 :(得分:6)

问题是,FG将输出直接重定向到程序的标准输出。见https://github.com/tomerfiliba/plumbum/blob/master/plumbum/commands.py#L611

当输出以这种方式重定向时,它不会通过plumbum的机器,所以你不会在异常对象中得到它。如果您愿意在slow_cmd完成之前阻止,那么更好的解决方案就是自己阅读stdout。这是草图:

lines = []
p = slow_cmd.popen()
while p.poll() is None:
    line = p.stdout.readline()
    lines.append(line)
    print line
if p.returncode != 0:
    print "see log file..."

更优雅的解决方案是编写自己的ExecutionModifier(如FG),它复制输出流。我们称之为TEE(在http://en.wikipedia.org/wiki/Tee_(command)之后)...我还没有对它进行测试,但是应该这样做(减去select stdout / err ):

class TEE(ExecutionModifier):
    def __init__(self, retcode = 0, dupstream = sys.stdout):
        ExecutionModifier.__init__(self, retcode)
        self.dupstream = dupstream
    def __rand__(self, cmd):
        p = cmd.popen()
        stdout = []
        stderr = []
        while p.poll():
            # note: you should probably select() on the two pipes, or make the pipes nonblocking,
            # otherwise readline would block
            so = p.stdout.readline()
            se = p.stderr.readline()
            if so:
                stdout.append(so)
                dupstream.write(so)
            if se:
                stderr.append(se)
                dupstream.write(se)
        stdout = "".join(stdout)
        stderr = "".join(stderr)
        if p.returncode != self.retcode:
            raise ProcessExecutionError(p.argv, p.returncode, stdout, stderr)
        return stdout, stderr

try:
    stdout, stderr = slow_cmd & TEE()
except ProcessExecutionError as e:
    pass # find the log file, etc.