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ć?


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")

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

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)

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)

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)

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
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.