Comment masquer la sortie d'un sous-processus dans Python 2.7

283

J'utilise eSpeak sur Ubuntu et j'ai un script Python 2.7 qui imprime et prononce un message:

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

eSpeak produit les sons souhaités, mais encombre le shell avec quelques erreurs (ALSA lib ..., pas de connexion de socket) donc je ne peux pas facilement lire ce qui a été imprimé plus tôt. Le code de sortie est 0.

Malheureusement, il n'y a pas d'option documentée pour désactiver sa verbosité, donc je cherche un moyen de le faire taire uniquement visuellement et de garder le shell ouvert propre pour une interaction ultérieure.

Comment puis-je faire ceci?

rypel
la source
ne pourriez-vous pas simplement appeler avec os.system alors? pas idéal mais ne devrait pas imprimer je ne pense pas
Joran Beasley
@JoranBeasley: os.system () s'imprime sur la console à moins que vous ne
redirigiez la
non, os.system ('espeak' + text) reproduit ce comportement.
rypel
@ferkulat: J'ai mis à jour ma réponse pour montrer également la os.systemsyntaxe. Bien que ce soit juste à titre d'illustration.
Rester
2
Version non 2.7 spécifique: stackoverflow.com/questions/5495078/… qui permet la subprocess.DEVNULsolution parfaite .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Réponses:

426

Redirigez la sortie vers DEVNULL:

import os
import subprocess

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

C'est en fait la même chose que d'exécuter cette commande shell:

retcode = os.system("echo 'foo' &> /dev/null")
jdi
la source
50
micro sélections soignées: vous pouvez utiliser os.devnullsi subprocess.DEVNULLn'est pas disponible (<3.3), utiliser check_call()au lieu de call()si vous ne vérifiez pas son code renvoyé, ouvrir les fichiers en mode binaire pour stdin/stdout/stderr, l'utilisation de os.system()doit être déconseillée, &>ne fonctionne pas shsur Ubuntu et explicite >/dev/null 2>&1pourrait être utilisé.
jfs
1
@JFSebastian: Merci pour les suggestions. Je voulais en fait l'utiliser, os.devnullmais je l'ai accidentellement tapé. De plus, je m'en tiens à l'utilisation des PO callcar ils n'attrapent pas l'exception possible check_call. Et pour la os.systemredirection, il s'agissait plus simplement d'une illustration de ce que fait l'utilisation efficace de l'approche du sous-processus. Pas vraiment comme deuxième suggestion.
jdi
13
Vous n'avez pas besoin de fermer le FNULL que vous avez ouvert?
Val
13
Juste une note, vous pouvez utiliser close_fds=Truein subprocess.callpour fermer le FNULLdescripteur après que le sous-processus existe
ewino
7
@ewino: On close_fds=True, les descripteurs de fichiers sont fermés après fork() mais avant, execvp() c'est-à-dire qu'ils sont fermés dans le processus enfant juste avant l'exécution de l'exécutable. close_fds=Truene fonctionnera pas sur Windows si l'un des flux est redirigé . close_fdsne ferme pas les fichiers dans le processus parent .
jfs
89

Voici une version plus portable (juste pour le plaisir, ce n'est pas nécessaire dans votre cas):

#!/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
la source
4
Notez que cela produit un DEVNULLqui n'est pas complètement général, comme celui fourni par subprocess; depuis qu'il est ouvert, wbil ne peut pas être utilisé stdin.
Reid
1
@Reid: vous pouvez utiliser le 'r+b'mode si vous en avez besoin à la place.
jfs
28

Utiliser subprocess.check_output(nouveau dans python 2.7). Il supprimera stdout et déclenchera une exception si la commande échoue. (Il retourne en fait le contenu de stdout, vous pouvez donc l'utiliser plus tard dans votre programme si vous le souhaitez.) Exemple:

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

Vous pouvez également supprimer stderr avec:

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

Pour les versions antérieures à 2.7, utilisez

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

Ici, vous pouvez supprimer stderr avec

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
la source
Plus précisément, il renvoie la sortie standard. Ce qui est génial car vous voudrez peut-être l'utiliser en plus de pouvoir l'ignorer.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Je pense que c'est plus simple. @StudentT Je pense que vous devriez gérer les erreurs avec CalledProcessError. except subprocess.CalledProcessError as epuis utilisez e.codeoue.output
Rodrigo E. Principe
21

Depuis Python3, vous n'avez plus besoin d'ouvrir devnull et vous pouvez appeler subprocess.DEVNULL .

Votre code serait mis à jour en tant que tel:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
la source
Travaux! De plus, peut échanger stderravec le stdoutcode ci-dessus (ou ajouter comme autre argument) pour supprimer les sorties. "Sorties" est dans le titre de la question et ce qui m'a amené ici ... peut-être trivial, mais j'ai pensé qu'il valait la peine d'être mentionné.
ThatsRightJack
-7

Pourquoi ne pas utiliser plutôt commands.getoutput ()?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
lolamontes69
la source
a) il ne supprime pas l'entrée, il l'accumule inutilement en mémoire b) il casse s'il textcontient des guillemets, ou utilise un codage de caractères différent, ou trop grand pour une ligne de commande c) il est Unix uniquement (sur Python 2)
jfs