包装子进程的stdout/stderr

10 投票
5 回答
7248 浏览
提问于 2025-04-16 07:56

我想通过Python的subprocess模块来捕获并显示一个进程的输出。

我以为可以直接把我的文件对象作为参数传给stdout和stderr。

我看到它访问了fileno属性,所以它确实在对这个对象做一些操作。不过,write()方法从来没有被调用。我的方法完全错了吗,还是我只是漏掉了什么?

class Process(object):
    class StreamWrapper(object):
        def __init__(self, stream):
            self._stream = stream
            self._buffer = []
        def _print(self, msg):
            print repr(self), msg
        def __getattr__(self, name):
            if not name in ['fileno']:
                self._print("# Redirecting: %s" % name)
            return getattr(self._stream, name)
        def write(self, data):
            print "###########"
            self._buffer.append(data)
            self._stream.write(data)
            self._stream.flush()
        def getBuffer(self):
            return self._buffer[:]
    def __init__(self, *args, **kwargs):
        print ">> Running `%s`" % " ".join(args[0])
        self._stdout = self.StreamWrapper(sys.stdout)
        self._stderr = self.StreamWrapper(sys.stderr)
        kwargs.setdefault('stdout', self._stdout)
        kwargs.setdefault('stderr', self._stderr)
        self._process = subprocess.Popen(*args, **kwargs)
        self._process.communicate()

更新:

我还希望能够使用ANSI控制字符来移动光标和覆盖之前输出的内容。我不知道这是不是正确的说法,但我举个例子:我想自动化一些GIT的操作,在那里进度会自我更新,而不是每次都换新的一行。

更新 2

对我来说,子进程的输出能够立即显示是很重要的。我尝试使用subprocess.PIPE来捕获输出并手动显示,但我只能在进程完成后才看到输出。然而,我希望能实时看到输出。

5 个回答

0

可以看看这里

p = subprocess.Popen(cmd,
                 shell=True,
                 bufsize=64,
                 stdin=subprocess.PIPE,
                 stderr=subprocess.PIPE,
                 stdout=subprocess.PIPE)
0

文件类型不够准确。它必须是真正的文件,并且要有一个真实的文件描述符。可以使用subprocess来处理管道,并根据需要从中读取数据。

12

一个程序的标准输入(stdin)、标准输出(stdout)和标准错误(stderr)需要是真正的文件描述符。(这其实不是Python的限制,而是操作系统层面上管道的工作方式。)所以你需要找其他的解决办法。

如果你想实时跟踪stdoutstderr,你就需要使用异步输入输出或者线程。

  • 异步输入输出:使用标准的同步(也就是阻塞)输入输出时,读取其中一个流可能会阻塞,这样就无法实时访问另一个流。如果你在Unix系统上,可以使用非阻塞输入输出,具体可以参考这个回答。不过在Windows上,这种方法可能就不太好用了。关于Python中的异步输入输出和一些替代方案,可以在这个视频中找到更多信息。

  • 线程:解决这个问题的另一种常见方法是为你想要实时读取的每个文件描述符创建一个线程。每个线程只处理它被分配的文件描述符,所以阻塞输入输出不会造成问题。

撰写回答