Como ocultar a saída do subprocesso no Python 2.7

283

Estou usando o eSpeak no Ubuntu e tenho um script Python 2.7 que imprime e fala uma mensagem:

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

O eSpeak produz os sons desejados, mas confunde o shell com alguns erros (ALSA lib ..., sem conexão de soquete), portanto não consigo ler facilmente o que foi impresso anteriormente. O código de saída é 0.

Infelizmente, não há uma opção documentada para desativar a verbosidade, portanto, estou procurando uma maneira de silenciá-lo visualmente e manter o shell aberto limpo para interação adicional.

Como posso fazer isso?

rypel
fonte
você não poderia simplesmente ligar com o os.system então? não é o ideal, mas não deve imprimir Eu não acho
Joran Beasley
@JoranBeasley: os.system () irá imprimir no console a menos que você redirecionar o comando shell
JDI
não, os.system ('falar' + texto) reproduz esse comportamento.
rypel
@ferkulat: Atualizei minha resposta para mostrar também a os.systemsintaxe. Embora seja apenas para ilustração. Ficar com subprocesso
jdi
2
Versão não específica da 2.7: stackoverflow.com/questions/5495078/…, que permite a subprocess.DEVNULsolução perfeita .
Ciro Santilli # 21/15

Respostas:

426

Redirecione a saída para DEVNULL:

import os
import subprocess

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

É efetivamente o mesmo que executar este comando do shell:

retcode = os.system("echo 'foo' &> /dev/null")
jdi
fonte
50
micro picks puras: você pode usar os.devnullse subprocess.DEVNULLnão estiver disponível (<3.3), usar em check_call()vez de call()não verificar o código retornado, abrir arquivos no modo binário stdin/stdout/stderr, o uso os.system()deve ser desencorajado, &>não funciona shno Ubuntu e explícito >/dev/null 2>&1pode ser usado.
usar o seguinte
1
@ JFSebastian: Obrigado pelas sugestões. Na verdade, eu pretendia usar, os.devnullmas acidentalmente digitei. Além disso, estou mantendo o uso dos POs, calljá que eles não estão capturando a possível exceção check_call. E para o os.systemredirecionamento, era mais apenas uma ilustração do que o uso efetivo da abordagem de subprocesso está fazendo. Não é realmente uma segunda sugestão.
JDI
13
Você não precisa fechar o FNULL que você abriu?
Val
13
Apenas uma nota, você pode usar close_fds=Trueem subprocess.callpara fechar o FNULLdescritor depois existe o subprocesso
ewino
7
@ewino: Ativado close_fds=True, os descritores de arquivos são fechados depois, fork() mas antes execvp() , ou seja, eles são fechados no processo filho logo antes da execução do executável. close_fds=Truenão funcionará no Windows se algum dos fluxos for redirecionado . close_fdsnão fecha arquivos no processo pai .
jfs
89

Aqui está uma versão mais portátil (apenas por diversão, não é necessário no seu 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
fonte
4
Observe que isso produz um DEVNULLque não é totalmente geral, como o fornecido por subprocess; desde que aberto wb, não pode ser usado stdin.
Reid
1
@ Reid: você pode usar o 'r+b'modo se precisar.
JFS
28

Use subprocess.check_output(novo no python 2.7). Suprimirá stdout e gerará uma exceção se o comando falhar. (Na verdade, ele retorna o conteúdo do stdout, para que você possa usá-lo posteriormente em seu programa, se desejar.) Exemplo:

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

Você também pode suprimir o stderr com:

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

Para versões 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

Aqui, você pode suprimir o stderr com

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
fonte
Mais precisamente, ele retorna o stdout. O que é ótimo, pois você pode querer usá-lo, além de poder ignorá-lo.
Ciro Santilli escreveu:
Eu acho que isso é mais direto. @ EstudanteT Eu acho que você deve lidar com erros com CalledProcessError. except subprocess.CalledProcessError as ee então use e.codeore.output
Rodrigo E. Principe
21

A partir do Python3, você não precisa mais abrir devnull e pode chamar subprocess.DEVNULL .

Seu código seria atualizado assim:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
fonte
Trabalho! Além disso, você pode trocar stderrcom o stdoutcódigo acima (ou acrescentar como outro argumento) para suprimir as saídas. "Outputs" está no título da pergunta e o que me levou até aqui ... talvez trivial, mas achei que valeria a pena mencionar.
ThatsRightJack
-7

Por que não usar commands.getoutput ()?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
lolamontes69
fonte
a) não descarta a entrada, acumula-a desnecessariamente na memória b) interrompe se texttiver aspas, ou usa uma codificação de caracteres diferente ou muito grande para uma linha de comando c) é apenas Unix (no Python 2)
jfs