如何在Python 2.7中隐藏子进程的输出


283

我在Ubuntu上使用eSpeak,并且有一个Python 2.7脚本可以打印和显示一条消息:

import subprocess
text = 'Hello World.'
print text
subprocess.call(['espeak', text])

eSpeak产生所需的声音,但由于一些错误(ALSA lib ...,没有套接字连接)而使外壳混乱,因此我无法轻松读取之前打印的内容。退出代码为0。

不幸的是,没有记录的选项可以关闭其详细信息,因此我正在寻找一种方法,仅在视觉上使其静音并保持打开的外壳干净以进行进一步的交互。

我怎样才能做到这一点?


那你不能只用os.system打电话吗?不理想,但我不认为应该打印
Joran Beasley 2012年

@JoranBeasley:os.system()将打印到控制台,除非您重定向shell命令
jdi 2012年

不,os.system('espeak'+ text)重现此行为。
rypel 2012年

@ferkulat:我更新了答案以显示os.system语法。虽然只是为了说明。坚持子流程
jdi 2012年

2
非2.7特定版​​本:stackoverflow.com/questions/5495078/…,它提供了理想的subprocess.DEVNUL解决方案。
西罗Santilli郝海东冠状病六四事件法轮功

Answers:


426

将输出重定向到DEVNULL:

import os
import subprocess

FNULL = open(os.devnull, 'w')
retcode = subprocess.call(['echo', 'foo'], stdout=FNULL, stderr=subprocess.STDOUT)

它实际上与运行此shell命令相同:

retcode = os.system("echo 'foo' &> /dev/null")

50
微整齐精选:你可以使用os.devnull,如果subprocess.DEVNULL不可用(<3.3),使用check_call()的不是call(),如果你不检查其返回代码,打开文件的二进制模式stdin/stdout/stderr,使用情况os.system()应当劝阻,&>不工作sh在Ubuntu的>/dev/null 2>&1可以使用明确的。
jfs 2012年

1
@JFSebastian:感谢您的建议。我实际上打算使用os.devnull但不小心将其键入。另外,我坚持使用OP,call因为它们没有捕获可能check_call引发的异常。对于os.system重定向,它只是说明子流程方法的有效使用正在做什么。确实不是第二个建议。
jdi 2012年

13
您是否不需要关闭已打开的FNULL?
2013年

13
只是注意,你可以使用close_fds=Truesubprocess.call关闭FNULL子进程存在后描述
ewino

7
@ewino:On close_fds=True,文件描述符在之后 fork()之前 关闭execvp()即在可执行程序运行之前进程中关闭它们。如果任何流被重定向,Windows上将无法使用。不会在进程中关闭文件。close_fds=Trueclose_fds
jfs

89

这是一个更便携的版本(只是为了好玩,在您的情况下没有必要):

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from subprocess import Popen, PIPE, STDOUT

try:
    from subprocess import DEVNULL # py3k
except ImportError:
    import os
    DEVNULL = open(os.devnull, 'wb')

text = u"René Descartes"
p = Popen(['espeak', '-b', '1'], stdin=PIPE, stdout=DEVNULL, stderr=STDOUT)
p.communicate(text.encode('utf-8'))
assert p.returncode == 0 # use appropriate for your program error handling here

4
请注意,这产生了一个DEVNULL不完全通用的,就像subprocess; 提供的一样。由于已打开wb,因此不能用于stdin
Reid

1
@Reid:'r+b'如果需要,可以使用mode。
jfs 2014年

28

使用subprocess.check_output(python 2.7中的新功能)。如果命令失败,它将抑制stdout并引发异常。(它实际上返回stdout的内容,因此您可以在以后的程序中使用它。)例如:

import subprocess
try:
    subprocess.check_output(['espeak', text])
except subprocess.CalledProcessError:
    # Do something

您还可以使用以下命令抑制stderr:

    subprocess.check_output(["espeak", text], stderr=subprocess.STDOUT)

对于2.7之前的版本,请使用

import os
import subprocess
with open(os.devnull, 'w')  as FNULL:
    try:
        subprocess._check_call(['espeak', text], stdout=FNULL)
    except subprocess.CalledProcessError:
        # Do something

在这里,您可以使用

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)

更准确地说,它返回标准输出。除了能够忽略它之外,它还很可能想同时使用它。
西罗Santilli郝海东冠状病六四事件法轮功

我认为这更直接。@StudentT我认为您应该使用CalledProcessError处理错误。except subprocess.CalledProcessError as e然后使用e.codee.output
Rodrigo E. Principe

21

由于Python3你不再需要打开devnull并且可以调用subprocess.DEVNULL

您的代码将这样更新:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)

作品!此外,可以换 stderrstdout在上面的代码(或追加作为另一参数)来抑制输出。问题的标题是“输出”,是什么导致我在这里...虽然微不足道,但认为值得一提。
ThatsRightJack

-7

为什么不使用commands.getoutput()代替呢?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)

a)它不会丢弃输入,它会不必要地将其存储在内存中b)如果其中text包含引号,或者使用了不同的字符编码,或者对于命令行而言太大,它都会中断c)它仅是Unix(在Python 2上)
jfs 2014年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.