So verbergen Sie die Ausgabe eines Unterprozesses in Python 2.7

283

Ich verwende eSpeak unter Ubuntu und habe ein Python 2.7-Skript, das eine Nachricht druckt und spricht:

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

eSpeak erzeugt die gewünschten Sounds, überfrachtet die Shell jedoch mit einigen Fehlern (ALSA lib ..., keine Socket-Verbindung), sodass ich nicht einfach lesen kann, was zuvor gedruckt wurde. Der Exit-Code ist 0.

Leider gibt es keine dokumentierte Option, um die Ausführlichkeit auszuschalten. Daher suche ich nach einer Möglichkeit, sie nur visuell zum Schweigen zu bringen und die offene Hülle für weitere Interaktionen sauber zu halten.

Wie kann ich das machen?

Rypel
quelle
Könntest du dann nicht einfach mit os.system anrufen? Nicht ideal, sollte aber nicht drucken, glaube ich nicht
Joran Beasley
@JoranBeasley: os.system () wird auf der Konsole gedruckt, es sei denn, Sie leiten den Shell-Befehl um
jdi
nein, os.system ('espeak' + text) reproduziert dieses Verhalten.
Rypel
@ferkulat: Ich habe meine Antwort aktualisiert, um auch die os.systemSyntax anzuzeigen . Obwohl es nur zur Veranschaulichung ist. Stick mit subprocess
jdi
2
Nicht 2.7-spezifische Version: stackoverflow.com/questions/5495078/…, die die perfekte subprocess.DEVNULLösung ermöglicht.
Ciro Santilli 法轮功 冠状 病 六四 事件 21

Antworten:

426

Leiten Sie die Ausgabe nach DEVNULL um:

import os
import subprocess

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

Es ist praktisch dasselbe wie das Ausführen dieses Shell-Befehls:

retcode = os.system("echo 'foo' &> /dev/null")
jdi
quelle
50
Micro Neat Picks: Sie könnten verwenden, os.devnullwenn subprocess.DEVNULLnicht verfügbar ist (<3.3), verwenden, check_call()anstatt, call()wenn Sie den zurückgegebenen Code nicht überprüfen, Dateien im Binärmodus öffnen für stdin/stdout/stderr, die Verwendung von os.system()sollte nicht empfohlen werden, &>funktioniert nicht shunter Ubuntu an explizit >/dev/null 2>&1könnte verwendet werden.
JFS
1
@JFSebastian: Danke für die Vorschläge. Eigentlich wollte ich es benutzen, habe es os.devnullaber versehentlich abgetippt. Außerdem halte ich mich an die Verwendung von OPs, callda sie die mögliche Ausnahme nicht abfangen check_callwürden. Und für die os.systemWeiterleitung war es eher ein Beispiel dafür, was die effektive Verwendung des Teilprozessansatzes bewirkt. Nicht wirklich als zweiter Vorschlag.
JDI
13
Müssen Sie den von Ihnen geöffneten FNULL nicht schließen?
Val
13
Nur eine Anmerkung, Sie können close_fds=Truein verwenden subprocess.call, um den FNULLDeskriptor zu schließen, nachdem der
Unterprozess
7
@ewino: On close_fds=True, Filedeskriptoren geschlossen sind nach fork() aber vor execvp() , dh sie in der geschlossenen Kind Prozess gerade bevor die ausführbare Datei ausgeführt wird. close_fds=Truefunktioniert unter Windows nicht, wenn einer der Streams umgeleitet wird . close_fdsschließt keine Dateien im übergeordneten Prozess.
JFS
89

Hier ist eine tragbarere Version (nur zum Spaß ist dies in Ihrem Fall nicht erforderlich):

#!/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
quelle
4
Beachten Sie, dass dies eine erzeugt, DEVNULLdie nicht ganz allgemein ist, wie die von subprocess; da es geöffnet ist wb, kann es nicht für verwendet werden stdin.
Reid
1
@Reid: Sie können den 'r+b'Modus verwenden, wenn Sie ihn stattdessen benötigen.
JFS
28

Verwenden Sie subprocess.check_output(neu in Python 2.7). Es unterdrückt stdout und löst eine Ausnahme aus, wenn der Befehl fehlschlägt. (Der Inhalt von stdout wird tatsächlich zurückgegeben, sodass Sie ihn später in Ihrem Programm verwenden können, wenn Sie möchten.) Beispiel:

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

Sie können stderr auch unterdrücken mit:

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

Verwenden Sie für früher als 2.7

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

Hier können Sie stderr mit unterdrücken

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
quelle
Genauer gesagt gibt es den Standard zurück. Das ist großartig, da Sie es vielleicht auch verwenden möchten, außer es ignorieren zu können.
Ciro Santilli 法轮功 冠状 病 六四 事件 18
Ich denke, das ist einfacher. @StudentT Ich denke, Sie sollten Fehler mit CalledProcessError behandeln. except subprocess.CalledProcessError as eund dann verwenden Sie e.codeodere.output
Rodrigo E. Principe
21

Ab Python3 Sie nicht mehr benötigen , um offene devnull und nennen subprocess.DEVNULL .

Ihr Code würde als solcher aktualisiert:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
quelle
Funktioniert! Kann außerdem stderrmit dem stdoutobigen Code ausgetauscht werden (oder als weiteres Argument anhängen), um Ausgaben zu unterdrücken. "Outputs" steht im Titel der Frage und was hat mich hierher geführt ... vielleicht trivial, aber ich dachte, es wäre erwähnenswert.
ThatsRightJack
-7

Warum nicht stattdessen command.getoutput () verwenden?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
lolamontes69
quelle
a) Es verwirft keine Eingaben, sammelt sie unnötig im Speicher. b) Es bricht ab, wenn textAnführungszeichen enthalten sind, oder verwendet eine andere Zeichenkodierung oder ist zu groß für eine Befehlszeile. c) Es ist nur Unix (unter Python 2).
JFS