如何抑制或捕获subprocess.run()的输出?

207

subprocess.run()文档中的示例来看,似乎不应该有任何输出。

subprocess.run(["ls", "-l"])  # doesn't capture output

然而,在Python shell中尝试时,列表会被打印出来。我想知道这是否是默认行为以及如何抑制run()的输出。


2
https://dev59.com/62oy5IYBdhLWcg3wdt4L - user2290362
4
subprocess.run()默认不会捕获标准输出或标准错误,要想捕获它们需要将PIPE传递给stdout和/或stderr参数(在链接的文档中有详细说明)。因此,除非你这样做,否则它们将像从其他进程一样正常显示。 - martineau
3
你想抑制输出还是捕获它? - SethMMorton
19
这个问题被标记为重复,但我认为这是一个错误,因为Python 2.7(process.call)和Python 3.5(process.run)之间的API发生了显着变化。我来到这里特意寻找Python 3的答案。这个问题的被接受的答案以及对它的顶级评论,在这种情况下比其他问题的答案更有用。 - GlennS
12
Python 3.7 起,你也可以简单地使用 capture_output=True 参数。 - Czechnology
显示剩余3条评论
2个回答

351

抑制

以下是按照净化级别递减顺序的输出抑制方法。假设您使用的是Python 3。

  1. 您可以重定向至特殊目标subprocess.DEVNULL
import subprocess

# To redirect stdout (only):
subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.DEVNULL
)

# to redirect stderr to /dev/null as well:
subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.DEVNULL,
    stderr = subprocess.DEVNULL
)

# Alternatively, you can merge stderr and stdout streams and redirect
# the one stream to /dev/null
subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.DEVNULL,
    stderr = subprocess.STDOUT
)

如果您想要一个完全手动的方法,可以通过自己打开文件句柄将其重定向到/dev/null。其他一切都与方法#1相同。
import os
import subprocess

with open(os.devnull, 'w') as devnull:
    subprocess.run(
        ['ls', '-l'],
        stdout = devnull
    )

捕获

以下是如何捕获输出(以便稍后使用或解析),按照清晰度递减的顺序排列。假设您正在使用Python 3。

注意:下面的示例使用universal_newlines=True(Python <= 3.6)。

  • 这将导致STDOUT和STDERR被捕获为str而不是bytes
    • 省略universal_newlines=True以获取bytes数据
  • Python >= 3.7接受text=True作为universal_newlines=True的简写形式
  1. 如果您只想独立捕获STDOUT和STDERR,并且您正在使用Python >= 3.7,请使用capture_output=True
import subprocess

result = subprocess.run(
    ['ls', '-l'],
    capture_output = True, # Python >= 3.7 only
    text = True # Python >= 3.7 only
)
print(result.stdout)
print(result.stderr)

你可以使用`subprocess.PIPE`来独立捕获标准输出和标准错误输出。这适用于任何支持`subprocess.run`的Python版本。
import subprocess

result = subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.PIPE,
    universal_newlines = True # Python >= 3.7 also accepts "text=True"
)
print(result.stdout)

# To also capture stderr...
result = subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.PIPE,
    stderr = subprocess.PIPE,
    universal_newlines = True # Python >= 3.7 also accepts "text=True"
)
print(result.stdout)
print(result.stderr)

# To mix stdout and stderr into a single string
result = subprocess.run(
    ['ls', '-l'],
    stdout = subprocess.PIPE,
    stderr = subprocess.STDOUT,
    universal_newlines = True # Python >= 3.7 also accepts "text=True"
)
print(result.stdout)

32
请注意,从Python 3.3版本开始,实际上有一个subprocess.DEVNULL,因此可以直接分配stdout参数而无需使用open,只需使用stdout=subprocess.DEVNULL - EquipDev
我在Python 3.7上遇到了“NameError:name 'devnull' is not defined”的错误。 - user9013730
1
@Sabrina 你定义了 devnull 吗? - SethMMorton
能否在同时捕获数据以供后续使用或解析的管道中打印输出? - Christopher
1
@Christopher 如果您不在程序运行时关心打印输出,那么您可以在程序运行后直接打印 result.stdout。如果您想要实时捕获和打印输出,那么就会稍微复杂一些,因为您不能使用 subprocess.run,而需要使用更底层的 subprocess.Popen。当我需要这样做时,我通常会使用类似于这个答案的方法(请根据 Python 3 调整语法)。 - SethMMorton
显示剩余7条评论

27

例如:捕获ls -a命令的输出结果。

import subprocess
ls = subprocess.run(['ls', '-a'], capture_output=True, text=True).stdout.strip("\n")
print(ls)

7
这适用于Python 3.7,但不适用于Python 3.6(我会收到“TypeError: init() got an unexpected keyword argument 'capture_output'”错误)。 - BCArg
最后一部分有助于将干净的输出传递到变量中。 - Lambert
在Flask中,如何使用“`”? - Алексей
1
@Алексей,我不确定你的意思。这个应该在Flask中正常工作,Flask仍然在普通的Python脚本中运行,并且与没有Flask的任何其他Python脚本一样可以访问文件系统。 - ggorlen

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