将子进程的错误输出重定向到标准输出流

52

我想将子进程的stderr输出重定向到stdout。常量STDOUT应该可以做到这一点,是吗?

但是,

$ python >/dev/null -c 'import subprocess;\
                        subprocess.call(["ls", "/404"],stderr=subprocess.STDOUT)'

为什么会输出一些东西。为什么会这样,我该如何在标准输出中获取错误信息?


6
顺便提一下,这个问题已经在Python 3.5中得到解决了。 - max
2个回答

51

在 Python < v3.5 中:

仔细阅读 源代码 就能得出答案。特别是当文档中说到:

subprocess.STDOUT
特殊值,表明标准错误输出应该进入与标准输出相同的句柄

由于在评估 stderr=subprocess.STDOUT 时,stdout 被设置为“默认”(技术上是 -1),因此 stderr 也被设置为“默认”。不幸的是,这意味着 stderr 输出仍然会流向 stderr。

为了解决这个问题,应该传递 stdout 文件,而不是 subprocess.STDOUT

$ python >/dev/null -c 'import subprocess,sys;subprocess.call(["ls", "/404"],
                        stderr=sys.stdout.buffer)'

或者,为了与 Python 旧版本 2.x 兼容:

$ python >/dev/null -c 'import subprocess,sys;subprocess.call(["ls", "/404"],
                        stderr=sys.stdout.fileno())'

2
一个人也可以将stderr保持不变:stderr=sys.stderr.fileno()。很好的例子;我将在所有我的脚本中简单地使用这种技术。我们有遗留的Python 2.x;因此,我会清楚地知道我告诉它去哪里而没有混淆。 - Mike S
2
记录一下,自 Python 3.5+ 起,这种情况已经不再必要了,因为它会被自动处理:https://github.com/python/cpython/blob/75c1ca7b6c546ef674eb40bfe47f41e6045dc99b/Lib/subprocess.py#L1143-L1147 - ipetrik

35

实际上,使用subprocess.STDOUT的作用就是按照文档所述将stderr重定向到stdout以便处理。


command = ["/bin/ls", "/tmp", "/notthere"]
process = subprocess.Popen(command, shell=False, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = ""
while (True):
    # Read line from stdout, break if EOF reached, append line to output
    line = process.stdout.readline()
    line = line.decode()
    if (line == ""): break
    output += line

结果存储在变量output中,其中包括进程从标准输出和标准错误输出获得的内容。

stderr=subprocess.STDOUT 将所有标准错误输出直接重定向到调用进程的标准输出上,这是一个主要区别。


编辑: 为更新的Python版本更新了代码:

command = ["/bin/ls", "/tmp", "/notthere"]
process = subprocess.Popen(command, shell=False, text=True, bufsize=1, stdout=subprocess.PIPE, stderr=subprocess.STDOUT)
output = ""
while (True):
    # Read line from stdout, break if EOF reached, append line to output
    line = process.stdout.readline()
    if (line == ""): break
    output += line

2
-1 这个案例与问题无关,问题是关于 stdoutNone。而且 subprocess.STDOUT 并不会重定向到“调用控制台”(我想你指的是当前进程所给出的文件句柄1),正如你自己的示例所显示的那样 - 如果你使用 ['ls', '/404'] 调用 Popen,则错误消息不会被显示 - phihag
1
你有一定的正确性。然而,我认为这并不是毫无关联的,因为这旨在澄清您关于文档误导的帖子 - 这并非事实。我本来会在您的帖子下留言的,但一年前我被禁止这样做。 - Fonic
1
python -c 'import subprocess,sys;subprocess.call(["ls", "/404"], stderr=subprocess.STDOUT)' 2>/dev/null 显示 ls 的 stderr 转到 stderr(文件描述符 2),因为您不会看到任何输出(假设您的系统上没有 /404)。Python 文档在这个主题上真的很糟糕,对于我们这些习惯于以无数种方式操作 stdout/stderr 的 shell 脚本编写者来说,这尤其令人恼火,我们必须来到 SO 这里才能弄清楚如何做一些简单而不会混淆的事情。 - Mike S
2
在我看来,上面的解决方案是更简单的,并且符合原帖作者的意图。 - Ray Salemi

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