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.