Jak ukryć dane wyjściowe podprocesu w Pythonie 2.7

283

Używam eSpeak na Ubuntu i mam skrypt Python 2.7, który drukuje i wypowiada komunikat:

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

eSpeak wydaje pożądane dźwięki, ale zaśmieca powłokę z pewnymi błędami (ALSA lib ..., brak połączenia z gniazdem), więc nie mogę łatwo odczytać tego, co zostało wcześniej wydrukowane. Kod zakończenia to 0.

Niestety nie ma udokumentowanej opcji wyłączenia jej gadatliwości, dlatego szukam sposobu, aby tylko wizualnie ją wyciszyć i utrzymać otwartą powłokę w czystości do dalszej interakcji.

W jaki sposób mogę to zrobić?

Rypel
źródło
czy mógłbyś wtedy nie tylko zadzwonić z os.system? nie idealny, ale nie powinienem drukować, nie sądzę
Joran Beasley
@JoranBeasley: os.system () wydrukuje na konsoli, chyba że przekierujesz polecenie powłoki
jdi
nie, os.system („espeak” + tekst) odtwarza to zachowanie.
rypel
@ferkulat: Zaktualizowałem moją odpowiedź, aby pokazać również os.systemskładnię. Ale to tylko dla ilustracji. Trzymaj się podprocesu
jdi
2
Wersja inna niż 2.7: stackoverflow.com/questions/5495078/..., która pozwala na idealne subprocess.DEVNULrozwiązanie.
Ciro Santilli 法轮功 冠状 病 六四 事件 法轮功 法轮功

Odpowiedzi:

426

Przekieruj wyjście do DEVNULL:

import os
import subprocess

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

W rzeczywistości działa to tak samo, jak uruchomienie tego polecenia powłoki:

retcode = os.system("echo 'foo' &> /dev/null")
jdi
źródło
50
micro neat picks: możesz użyć, os.devnulljeśli subprocess.DEVNULLnie jest dostępny (<3.3), użyj check_call()zamiast, call()jeśli nie sprawdzasz zwróconego kodu, otwieraj pliki w trybie binarnym stdin/stdout/stderr, korzystanie z os.system()powinno być odradzane, &>nie działa shna Ubuntu i >/dev/null 2>&1można użyć jawnego .
jfs
1
@JFSebastian: Dzięki za sugestie. Chciałem go użyć, os.devnullale przypadkowo go napisałem. Ponadto trzymam się użycia PO, callponieważ nie wychwytują potencjalnego wyjątku, check_callktóry mógłby wystąpić. W przypadku os.systemprzekierowania była to raczej tylko ilustracja tego, co robi skuteczne wykorzystanie podejścia podprocesowego. Niezupełnie jako druga sugestia.
jdi
13
Nie musisz zamykać FNULL, który otworzyłeś?
Val
13
Tylko uwaga, można użyć close_fds=Truew subprocess.callcelu zamknięcia FNULLdeskryptor po podproces istnieje
ewino
7
@ewino: Włączone close_fds=True, deskryptory plików są zamykane później, fork() ale wcześniej, execvp() tzn. są zamykane w procesie potomnym tuż przed uruchomieniem pliku wykonywalnego. close_fds=Truenie będzie działać w systemie Windows, jeśli którykolwiek ze strumieni zostanie przekierowany . close_fdsnie zamyka plików w procesie nadrzędnym .
jfs
89

Oto bardziej przenośna wersja (dla zabawy, nie jest to konieczne w twoim przypadku):

#!/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
źródło
4
Zauważ, że daje to wynik, DEVNULLktóry nie jest w pełni ogólny, jak ten dostarczony przez subprocess; ponieważ jest otwarty, wbnie można go używać stdin.
Reid
1
@Reid: możesz użyć 'r+b'trybu, jeśli go potrzebujesz.
jfs
28

Użyj subprocess.check_output(nowość w Pythonie 2.7). Pominie stdout i zgłosi wyjątek, jeśli polecenie się nie powiedzie. (W rzeczywistości zwraca zawartość standardowego wyjścia, więc możesz użyć tego później w swoim programie, jeśli chcesz). Przykład:

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

Możesz również stderr stłumić za pomocą:

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

W przypadku wersji wcześniejszych niż 2.7 użyj

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

Tutaj możesz stderr za pomocą

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
źródło
Dokładniej, zwraca standardowe wyjście. Co jest świetne, ponieważ może chcieć z niego korzystać, oprócz tego, że mogę go zignorować.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Myślę, że jest to prostsze. @StudentT Myślę, że powinieneś obsługiwać błędy w CalledProcessError. except subprocess.CalledProcessError as ea następnie użyj e.codelube.output
Rodrigo E. Principe
21

Począwszy od Python3, nie musisz już otwierać devnull i możesz wywoływać subprocess.DEVNULL .

Twój kod zostanie zaktualizowany jako taki:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
źródło
Pracuje! Ponadto można zamienić stderrz stdoutpowyższym kodem (lub dołączyć jako kolejny argument), aby ukryć wyjścia. „Wyjścia” są w tytule pytania i co mnie tu doprowadziło… może trywialne, ale pomyślałem, że warto o tym wspomnieć.
ThatsRightJack
-7

Dlaczego zamiast tego nie użyć poleceń .getoutput ()?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
lolamontes69
źródło
a) nie odrzuca danych wejściowych, niepotrzebnie gromadzi je w pamięci b) łamie się, jeśli textzawiera cudzysłowy, używa innego kodowania znaków lub jest zbyt duży dla wiersza poleceń c) jest tylko Uniksem (w Pythonie 2)
jfs