How to hide output of subprocess
Asked Answered
M

5

374

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?

Manganate answered 29/6, 2012 at 22:8 Comment(6)
could you not just call with os.system then? not ideal but shouldnt print i dont thinkAttestation
@JoranBeasley: os.system() will print to the console unless you redirect the shell commandCallihan
no, os.system('espeak '+ text) reproduces this behavior.Manganate
@ferkulat: I updated my answer to also show the os.system syntax. Though it is just for illustration. Stick with subprocessCallihan
good correction ... your right ... for some reason i remembered incorrectly thinking that os.system didnt print ...Attestation
Non 2.7 specific version: #5495578 which allows for the perfect subprocess.DEVNUL solution.Archaism
C
590

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")
Callihan answered 29/6, 2012 at 22:15 Comment(16)
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.Pollard
@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.Callihan
Don't you need to close the FNULL that you have opened?Vlad
Just a note, you can use close_fds=True in subprocess.call to close the FNULL descriptor after the subprocess existsElise
@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.Callihan
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.Portly
@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.Elise
@Elise 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.Callihan
@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. close_fds does not close files in the parent process.Pollard
Similar answer here, except inline and byte mode too: https://mcmap.net/q/25661/-is-there-a-quiet-version-of-subprocess-callRegister
great answer for python 3!Billingsgate
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)Coverup
@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 introduceCallihan
@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.Callihan
can it be called under subprocess.check_output()?Aeolus
@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.Callihan
P
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
Pollard answered 30/6, 2012 at 1:2 Comment(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.Cherubini
@Reid: you could use 'r+b' mode if you need it instead.Pollard
@Pollard 8 years later we just do stdout=subprocess.DEVNULL, right?Anastice
the code is Python 2/3 compatible. If you don't need Python 2, then the import: from subprocess import DEVNULL is enough.Pollard
T
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)
Thief answered 14/7, 2016 at 21:32 Comment(3)
More precisely, it returns the stdout. Which is great as might want to use it as well besides being able to ignore it.Archaism
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.outputHeavy
this was useful for more complex shell commands using variables & communicating with a REST service (subprocess.run did not work in my case)Kingery
C
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)
Chunk answered 23/7, 2018 at 21:56 Comment(1)
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.Fourdimensional
R
-7

Why not use commands.getoutput() instead?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
Rebecarebecca answered 31/8, 2014 at 22:40 Comment(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)Pollard

© 2022 - 2024 — McMap. All rights reserved.