Come nascondere l'output del sottoprocesso in Python 2.7


283

Sto usando eSpeak su Ubuntu e ho uno script Python 2.7 che stampa e pronuncia un messaggio:

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

eSpeak produce i suoni desiderati, ma ingombra la shell con alcuni errori (ALSA lib ..., nessuna connessione socket), quindi non riesco a leggere facilmente ciò che è stato stampato in precedenza. Il codice di uscita è 0.

Sfortunatamente non esiste un'opzione documentata per disattivare la sua verbosità, quindi sto cercando un modo per silenziarlo solo visivamente e mantenere pulito il guscio aperto per ulteriori interazioni.

Come posso fare questo?


allora non potresti semplicemente chiamare con os.system? non è l'ideale, ma non dovrei stampare non credo
Joran Beasley,

@JoranBeasley: os.system () stamperà sulla console a meno che non si reindirizzi il comando shell
jdi

no, os.system ('espeak' + testo) riproduce questo comportamento.
rypel,

@ferkulat: ho aggiornato la mia risposta per mostrare anche la os.systemsintassi. Anche se è solo a scopo illustrativo. Stick with subprocess
jdi

2
Versione non 2.7 specifica: stackoverflow.com/questions/5495078/… che consente la subprocess.DEVNULsoluzione perfetta .
Ciro Santilli 21 冠状 病 六四 事件 法轮功

Risposte:


426

Reindirizzare l'output su DEVNULL:

import os
import subprocess

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

È effettivamente lo stesso dell'esecuzione di questo comando shell:

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

50
scelte micro ordinate: è possibile utilizzare os.devnullse subprocess.DEVNULLnon è disponibile (<3.3), utilizzare check_call()invece call()se non si controlla il codice restituito, aprire i file in modalità binaria per stdin/stdout/stderr, l'uso di os.system()dovrebbe essere scoraggiato, &>non funziona shsu Ubuntu un esplicito >/dev/null 2>&1potrebbe essere utilizzato.
jfs,

1
@JFSebastian: grazie per i suggerimenti. In realtà intendevo usarlo os.devnullma l'ho scritto per errore. Inoltre, sto rispettando l'uso dei PO callpoiché non stanno rilevando la possibile eccezione check_callsollevata. E per il os.systemreindirizzamento, era più semplicemente un'illustrazione di ciò che sta facendo l'uso efficace dell'approccio del sottoprocesso. Non proprio come un secondo suggerimento.
jdi,

13
Non hai bisogno di chiudere il FNULL che hai aperto?
Val

13
Solo una nota, puoi usare close_fds=Truein subprocess.callper chiudere il FNULLdescrittore dopo che il sottoprocesso esiste
ewino,

7
@ewino: Attivo close_fds=True, i descrittori di file vengono chiusi dopo fork() ma prima execvp() , vale a dire, vengono chiusi nel processo figlio appena prima dell'esecuzione dell'eseguibile. close_fds=Truenon funzionerà su Windows se uno dei flussi viene reindirizzato . close_fdsnon chiude i file nel processo principale .
jfs

89

Ecco una versione più portatile (solo per divertimento, non è necessario nel tuo 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

4
Si noti che questo produce un DEVNULLnon completamente generale, come quello fornito da subprocess; poiché è aperto wbnon può essere utilizzato per stdin.
Reid

1
@Reid: puoi usare la 'r+b'modalità se ne hai bisogno.
jfs,

28

Usa subprocess.check_output(nuovo in Python 2.7). Sopprimerà stdout e solleverà un'eccezione se il comando fallisce. (In realtà restituisce il contenuto di stdout, quindi puoi usarlo in seguito nel tuo programma se vuoi.) Esempio:

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

Puoi anche sopprimere stderr con:

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

Per prima di 2.7, utilizzare

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

Qui puoi sopprimere stderr con

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

Più precisamente, restituisce lo stdout. Il che è fantastico come si potrebbe desiderare di usarlo oltre a poterlo ignorare.
Ciro Santilli 18 冠状 病 六四 事件 法轮功

Penso che questo sia più semplice. @StudentT Penso che dovresti gestire gli errori con CalledProcessError. except subprocess.CalledProcessError as ee poi usa e.codeoe.output
Rodrigo E. Principe

21

A partire da Python3 non devi più aprire devnull e puoi chiamare subprocess.DEVNULL .

Il tuo codice verrebbe aggiornato come tale:

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

Lavori! Inoltre, è possibile scambiare stderrcon il stdoutcodice in sopra (o aggiungere come altro argomento) per sopprimere gli output. "Outputs" è nel titolo della domanda e cosa mi porta qui ... forse banale, ma ho pensato che valesse la pena menzionarlo.
ThatsRightJack

-7

Perché non usare comandi.getoutput () invece?

import commands

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

a) non scarta l'input, lo accumula inutilmente in memoria b) si rompe se textcontiene virgolette o utilizza una codifica di caratteri diversa o troppo grande per una riga di comando c) è solo Unix (su Python 2)
jfs,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.