在Python调用可执行文件时如何抑制输出

45

我有一个名为A的二进制文件,调用时会生成输出。如果从Bash shell中调用它,大部分输出会被A > /dev/null抑制,而A &> /dev/null则会将所有输出都抑制。

我有一个名为B的Python脚本需要调用A,我想要在从B生成输出的同时抑制A的所有输出。

B中,我尝试了os.system('A')os.system('A > /dev/null')os.system('A &> /dev/null')os.execvp('...')等方法,但这些方法都不能完全抑制A的所有输出。

我可以运行B &> /dev/null,但这也会抑制B的所有输出,我不希望这样。

有没有人有建议?

9个回答

102
import os
import subprocess

command = ["executable", "argument_1", "argument_2"]

with open(os.devnull, "w") as fnull:
    result = subprocess.call(command, stdout = fnull, stderr = fnull)
如果命令没有任何参数,你可以将其提供为简单的字符串。
如果你的命令依赖于shell特性,例如通配符、管道或环境变量,那么你需要将整个命令作为字符串提供,并且指定shell=True。尽管如此,应该避免这样做,因为如果未对字符串内容进行仔细验证,则会带来安全风险。

4
因为原问题使用了os.system且不确定具体操作,所以shell=True是最可靠的翻译。 - DNS
DNS,我尝试了你的解决方案,它对我来说完美地运行。谢谢! - Lin
7
那又怎样?很多人使用os.system()而没有意识到他们不必通过shell。现在,因为你没有提供更安全的选择,他可能会无故地使用shell=True。请注意,这可能存在安全风险。 - Devin Jeanpierre
-1 如果subprocess.call抛出错误,这也会泄漏文件句柄。如果可用的话,你应该真正使用subprocess.DEVNULL - phihag
6
我试图回答问题,使用最少的代码,以一种在当时大多数语言版本中都可以工作的方式。但是,考虑到答案的受欢迎程度,您是正确的,我不应该给新来访者带来错误的想法,因此我已编辑它以反映更好的实践方法。然而,我不会用Python 3.3中才出现的东西去困惑人们,而且这与我上面的代码在功能上是等效的;那太可笑了。 - DNS

56
如果您使用Python 2.4,您可以使用subprocess模块
>>> import subprocess
>>> s = subprocess.Popen(['cowsay', 'hello'], \
      stderr=subprocess.STDOUT, stdout=subprocess.PIPE).communicate()[0]
>>> print s
 _______ 
< hello >
 ------- 
        \   ^__^
         \  (oo)\_______
            (__)\       )\/\
                ||----w |
                ||     ||

7
如果标准输出的输出没有边界,那么这将表现不佳。 - David Foster
2
cowsay自1999年以来就存在了,我怎么从未听说过它? - LarsH
我今天才听说过这样的程序。 - answerSeeker

21
在Python 3.3及更高版本中,subprocess支持将输出重定向到/dev/null的选项。使用时,在调用.Popen和相关函数时,指定stdout=subprocess.DEVNULL, stderr=subprocess.DEVNULL作为关键字参数即可。
因此,DNS的答案在Python 3.3+中重写为:
import subprocess
command = ["executable", "argument_1", "argument_2"]
result = subprocess.call(command,
                         stdout=subprocess.DEVNULL,
                         stderr=subprocess.DEVNULL)

从文档中得知:

subprocess.DEVNULL¶

特殊值,可用作stdin、stdout或stderr参数传递给Popen,表示将使用特殊文件os.devnull。

自3.3版本开始新增。

对于Python 3.0到3.2版本,您需要手动使用open(os.devnull)打开空设备,如DNS所述。


1
很遗憾,subprocess.DEVNULL只在3.3及以上版本中可用。这个答案应该补充兼容代码(或参考代码)。也没有理由使用shell=True - phihag
@phihag:我没有包含兼容性代码,因为DNS的答案已经充分描述了如何在不使用subprocess.DEVNULL的情况下完成它。你关于shell的观点是正确的;已经修复。 - Mechanical snail

12
如果你通过搜索引擎找到了这个旧问题(就像我一样),请注意使用 PIPE 可能会导致死锁问题。 因为管道是带缓冲区的,所以即使没有人读取,你也可以向管道写入一定数量的字节。然而,缓冲区的大小是有限的。因此,如果程序 A 的输出大于缓冲区,则在调用程序 B 等待 A 终止时,A 将被阻塞在写入操作上。但是,在这种特殊情况下不会发生死锁,请参见下面的评论。 仍然建议使用 Devin Jeanpierre 和 DNS 的解决方案。

这是真的吗?我认为只有在使用call()或popen()/wait()时才会出现死锁,而不是popen()/communicate()。 - John Gordon
是的!引用文档中的话,“注意,读取的数据被缓存在内存中,因此如果数据大小很大或无限制,请勿使用此方法。” - Po' Lazarus
“缓存在内存中”并不一定意味着管道已经死锁。文档中提到的问题是,如果一个命令产生了大量输出,它们都会被存储在Python堆上的内存中。当然,这是不可取的,可能会导致内存不足错误或者性能非常差,但只有在极端情况下才会发生,并且您不会遇到死锁问题。 - Clueless
1
首先,我们应该同意,如果您对程序的实际输出不感兴趣,那么您不需要使用 communicate,并且在这种情况下,使用 devnull 是一个更好的选择。其次,看起来您是完全正确的:communicate 能够小心地完成它的工作,而且不会死锁。 - Po' Lazarus

8

正如os.system()文档所提到的,使用subprocess模块,并且在打开子进程时,如果需要可以设置stdout=open(os.devnull, 'w')(也许对于stderr也是一样的)。


1

我知道现在已经有点晚了,但为什么不直接从os.system中将输出重定向到/dev/null呢?例如:

tgt_file = "./bogus.txt"
os.sytem("d2u '%s' &> /dev/null" % tgt_file)

这似乎适用于那些不想处理subprocess.STDOUT的情况。


2
因为使用 os.system 进行系统调用已经被弃用。subprocess 模块以更加优雅和安全的方式处理这个问题。 - MestreLion

0

如果你只需要捕获STDOUT,将其分配给一个变量不就可以了吗?例如:

megabyte=''
# Create a 1 MiB string of NULL characters.
for i in range(1048576):
    megabyte += '\0'
fh=open('zero.bin','w')
# Write an 8 GiB file.
for i in range(8192):
    print(i)
    # Suppress output of 'write()' by assigning to a variable.
    discard=fh.write(megabyte)
fh.close()

我正在创建一个大的零填充文件以清空硬盘上的空闲空间,并发现每次调用 handle.write(string) 都会输出写入的字节数。将其分配给一个变量可以阻止该输出。


0

如果您不想等待命令完成,比如启动备份任务,另一个选项是通过bash传递它,这样可以使重定向正常运行。

例如,使用aplay启动声音文件:

import os

def PlaySound(filename):
    command = 'bash -c "aplay %s &> /dev/null &"' % (filename)
    os.system(command)

这样我就可以生成一个新的进程,而不必等待它完成并阻止它在终端上打印输出。 唯一的问题是它会加载一个bash实例以及你正在运行的进程,从而提供了轻微的开销。


0

我使用:

call(command, stderr=subprocess.PIPE, stdout=subprocess.PIPE)

命令是指命令字符串加上参数

为了使其正常工作,您必须导入subprocess模块


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