Cómo ocultar la salida del subproceso en Python 2.7

283

Estoy usando eSpeak en Ubuntu y tengo un script Python 2.7 que imprime y dice un mensaje:

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

eSpeak produce los sonidos deseados, pero satura el shell con algunos errores (ALSA lib ..., no hay conexión de socket), por lo que no puedo leer fácilmente lo que se imprimió anteriormente. El código de salida es 0.

Desafortunadamente, no hay una opción documentada para desactivar su verbosidad, por lo que estoy buscando una manera de silenciarlo visualmente y mantener limpio el shell abierto para una mayor interacción.

¿Cómo puedo hacer esto?

rypel
fuente
¿no podrías simplemente llamar con os.system entonces? no es ideal pero no debería imprimir, no creo
Joran Beasley
@JoranBeasley: os.system () se imprimirá en la consola a menos que redirija el comando de shell
jdi
no, os.system ('espeak' + text) reproduce este comportamiento.
rypel
@ferkulat: Actualicé mi respuesta para mostrar también la os.systemsintaxis. Aunque es solo para ilustración. Seguir con el subproceso
jdi
2
Versión específica no 2.7: stackoverflow.com/questions/5495078/… que permite la subprocess.DEVNULsolución perfecta .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Respuestas:

426

Redireccionar la salida a DEVNULL:

import os
import subprocess

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

Es efectivamente lo mismo que ejecutar este comando de shell:

retcode = os.system("echo 'foo' &> /dev/null")
jdi
fuente
50
selecciones micro ordenadas: puede usar os.devnullsi subprocess.DEVNULLno está disponible (<3.3), usar en check_call()lugar de call()si no verifica su código devuelto, abrir archivos en modo binario para stdin/stdout/stderr, el uso de os.system()debe desalentarse, &>no funciona shen Ubuntu un explícito >/dev/null 2>&1podría ser utilizado.
jfs
1
@JFSebastian: Gracias por las sugerencias. En realidad tenía la intención de usar, os.devnullpero accidentalmente lo escribí. Además, me quedo con el uso de los OP, callya que no están atrapando la posible excepción check_callque aumentaría. Y para la os.systemredirección, fue más que una ilustración de lo que está haciendo el uso efectivo del enfoque de subproceso. No realmente como una segunda sugerencia.
jdi
13
¿No necesitas cerrar el FNULL que has abierto?
Val
13
Sólo una nota, se puede utilizar close_fds=Trueen subprocess.callpara cerrar el FNULLdescriptor después existe el sub
ewino
77
@ewino: activado close_fds=True, los descriptores de archivo se cierran después fork() pero antes execvp() , es decir, se cierran en el proceso secundario justo antes de que se ejecute el ejecutable. close_fds=Trueno funcionará en Windows si alguna de las transmisiones se redirige . close_fdsno cierra archivos en el proceso padre .
jfs
89

Aquí hay una versión más portátil (solo por diversión, no es necesario en su caso):

#!/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
fuente
44
Tenga en cuenta que esto produce un DEVNULLque no es totalmente general, como el proporcionado por subprocess; ya que está abierto wbno se puede usar para stdin.
Reid
1
@Reid: podría usar el 'r+b'modo si lo necesita en su lugar.
jfs
28

Uso subprocess.check_output(nuevo en python 2.7). Suprimirá stdout y generará una excepción si el comando falla. (En realidad, devuelve el contenido de stdout, por lo que puede usarlo más adelante en su programa si lo desea). Ejemplo:

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

También puede suprimir stderr con:

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

Para versiones anteriores a 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

Aquí, puede suprimir stderr con

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
fuente
Más precisamente, devuelve el stdout. Lo cual es genial, ya que podría querer usarlo además de poder ignorarlo.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Creo que esto es más sencillo. @StudentT Creo que debería manejar los errores con CalledProcessError. except subprocess.CalledProcessError as ey luego use e.codeoe.output
Rodrigo E. Principe
21

A partir de Python3 ya no necesita abrir devnull y puede llamar a subprocess.DEVNULL .

Su código se actualizará como tal:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
fuente
¡Trabajos! Además, puede intercambiar stderrcon el stdoutcódigo anterior (o agregar como otro argumento) para suprimir las salidas. "Resultados" está en el título de la pregunta y lo que me llevó aquí ... tal vez trivial, pero pensé que valía la pena mencionarlo.
ThatsRightJack
-7

¿Por qué no usar command.getoutput () en su lugar?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
lolamontes69
fuente
a) no descarta la entrada, la acumula innecesariamente en la memoria b) se rompe si texttiene comillas, o usa una codificación de caracteres diferente, o demasiado grande para una línea de comando c) es solo Unix (en Python 2)
jfs