365

I'm using eSpeak on Ubuntu and have a Python 2.7 script that prints and speaks a message:

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

eSpeak produces the desired sounds, but clutters the shell with some errors (ALSA lib..., no socket connect) so i cannot easily read what was printed earlier. Exit code is 0.

Unfortunately there is no documented option to turn off its verbosity, so I'm looking for a way to only visually silence it and keep the open shell clean for further interaction.

How can I do this?

Slim
  • 648
  • 4
  • 15
rypel
  • 4,686
  • 2
  • 25
  • 36

5 Answers5

574

For python >= 3.3, Redirect the output to DEVNULL:

import os
import subprocess

retcode = subprocess.call(['echo', 'foo'], 
    stdout=subprocess.DEVNULL,
    stderr=subprocess.STDOUT)

For python <3.3, including 2.7 use:

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

It is effectively the same as running this shell command:

retcode = os.system("echo 'foo' &> /dev/null")
legends2k
  • 31,634
  • 25
  • 118
  • 222
jdi
  • 90,542
  • 19
  • 167
  • 203
  • 57
    micro neat picks: you could use `os.devnull` if `subprocess.DEVNULL` is not available (<3.3), use `check_call()` instead of `call()` if you don't check its returned code, open files in binary mode for `stdin/stdout/stderr`, usage of `os.system()` should be discouraged, `&>` doesn't work for `sh` on Ubuntu an explicit `>/dev/null 2>&1` could be used. – jfs Jun 30 '12 at 01:02
  • 2
    @J.F.Sebastian: Thanks for the suggestions. I actually meant to use `os.devnull` but accidentally typed it out. Also, I am sticking with the OPs use of `call` since they are not catching the possible exception `check_call` would raise. And for the `os.system` redirect, it was more just an illustration of what the effective use of the subprocess approach is doing. Not really as a second suggestion. – jdi Jun 30 '12 at 01:13
  • 15
    Don't you need to close the FNULL that you have opened? – Val Nov 11 '13 at 12:47
  • 13
    Just a note, you can use `close_fds=True` in `subprocess.call` to close the `FNULL` descriptor after the subprocess exists – ewino Jul 06 '14 at 10:27
  • @ewino, I thought close_fds is to have inherited file descriptors closed in the forked process. Not for it to close anything on the parent side. – jdi Jul 06 '14 at 20:01
  • Can't you just put a try block arround your code and in the finally block do: FNULL.close() ? So it is always closed no matter what. – Bosiwow Jul 07 '14 at 09:44
  • @jdi, you're probably right. I read the module's code and it looked that way, but the documentation does specify it closes the child's descriptors. – ewino Jul 07 '14 at 12:03
  • 1
    @ewino exactly. It closes file descriptors in the child to prevent it from Inheriting all of the parent descriptors besides stdin, stdout, stderr. I don't really get why there is a conversation going on about how to close the file handle. The fact is that I only wrote the line about calling subprocess to be concise. Yes obviously you need to close any file handles you open. I just didn't think I needed to write a wider example. You can close it manually, or with a try/finally, or using a with-context. – jdi Jul 07 '14 at 19:40
  • 7
    @ewino: On `close_fds=True`, file descriptors are closed *after* `fork()` but *before* `execvp()` i.e., they are closed in the *child* process just *before* the executable is run. [`close_fds=True` won't work on Windows if any of the streams are redirected](https://www.python.org/dev/peps/pep-0446/#inheritance-of-file-descriptors-on-windows). `close_fds` does not close files in the *parent* process. – jfs Apr 07 '15 at 07:48
  • Similar answer here, except inline and byte mode too: http://stackoverflow.com/a/8529412/1695680 – ThorSummoner Feb 01 '16 at 20:19
  • 1
    great answer for python 3! – Edeph Oct 19 '16 at 13:24
  • Seems it's not always possible to use this. It doesn't work on python 2.7 (windows7) with 7zip - child process (7zip) fails immediately if `stderr` is changed to `FNULL` (or `stdout=FNULL, stderr=subprocess.STDOUT`). (moght be something specific to 7zip) – industryworker3595112 Nov 28 '17 at 11:08
  • @industryworker3595112, it is definitely a general solution that works. I can't confirm this specifically for windows + 7zip. I'm not familiar with whatever quirks windows+7zip could introduce – jdi Nov 28 '17 at 20:32
  • @ZeeshanAhmad it's an old and primitive way of running a command that gives little control over the subprocess. The subprocess module unified all those older styles. For instance, you can't get the pid and kill the process if needed. You cant get output and the only way to control things like the output streams or env is in the command formatting. – jdi Jan 11 '19 at 21:17
  • can it be called under `subprocess.check_output()`? – alper Apr 13 '22 at 10:24
  • 1
    @alper, sure. They are just wrappers around basically the same thing with different default options. `check_output` defaults to capturing stdout to a pipe and returning the data, which is slightly different than piping it to devnull to be discarded. You still probably want to set stderr as appropriate. – jdi Apr 14 '22 at 12:21
106

Here's a more portable version (just for fun, it is not necessary in your case):

#!/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
jfs
  • 399,953
  • 195
  • 994
  • 1,670
  • 4
    Note that this produces a `DEVNULL` which isn't fully general, like the one provided by `subprocess`; since it's opened `wb` it can't be used for `stdin`. – Reid Dec 01 '14 at 17:23
  • 1
    @Reid: you could use `'r+b'` mode if you need it instead. – jfs Dec 01 '14 at 17:27
  • 3
    @jfs 8 years later we just do `stdout=subprocess.DEVNULL`, right? – Boris Verkhovskiy Oct 03 '20 at 07:27
  • 1
    the code is Python 2/3 compatible. If you don't need Python 2, then the import: `from subprocess import DEVNULL` is enough. – jfs Oct 03 '20 at 17:00
35

Use subprocess.check_output (new in python 2.7). It will suppress stdout and raise an exception if the command fails. (It actually returns the contents of stdout, so you can use that later in your program if you want.) Example:

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

You can also suppress stderr with:

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

For earlier than 2.7, use

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

Here, you can suppress stderr with

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
  • 37,389
  • 14
  • 105
  • 140
  • More precisely, it returns the stdout. Which is great as might want to use it as well besides being able to ignore it. – Ciro Santilli OurBigBook.com Aug 18 '16 at 14:01
  • I think this is more straightforward. @StudentT I think you should handle errors with CalledProcessError. `except subprocess.CalledProcessError as e` and then use `e.code` or `e.output` – Rodrigo E. Principe Mar 10 '17 at 12:42
  • this was useful for more complex `shell` commands using variables & communicating with a `REST` service (`subprocess.run` did not work in my case) – Stuart Cardall Jun 05 '22 at 21:06
33

As of Python3 you no longer need to open devnull and can call subprocess.DEVNULL.

Your code would be updated as such:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
  • 3,807
  • 3
  • 33
  • 50
  • 2
    Works! In addition, can swap `stderr` with `stdout` in code above (or append as another argument) to suppress outputs. "Outputs" is in the title of the question and what lead me here...maybe trivial, but thought it was worth mentioning. – ThatsRightJack May 06 '20 at 12:17
-7

Why not use commands.getoutput() instead?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
  • 1
    a) it doesn't discard input, it accumulates it in memory unnecessarily b) it breaks if `text` has quotes in it, or uses a different character encoding, or too large for a command line c) it is Unix only (on Python 2) – jfs Sep 09 '14 at 10:56