Як приховати вихід підпроцесу в Python 2.7


283

Я використовую eSpeak в Ubuntu і маю скрипт Python 2.7, який друкує та промовляє повідомлення:

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

eSpeak видає бажані звуки, але захаращує оболонку з деякими помилками (ALSA lib ..., немає підключення сокета), тому я не можу легко прочитати те, що було надруковано раніше. Код виходу 0.

На жаль, немає документально підтвердженого варіанту вимкнути його багатослів’я, тому я шукаю спосіб лише візуально заглушити його та зберегти чисту відкриту оболонку для подальшої взаємодії.

Як я можу це зробити?


ви могли б не просто зателефонувати з os.system тоді? не ідеально, але не варто друкувати, я не думаю
Joran Beasley

@JoranBeasley: os.system () надрукує на консоль, якщо ви не перенаправите команду оболонки
jdi

ні, os.system ('espeak' + текст) відтворює цю поведінку.
рипель

@ferkulat: Я оновив свою відповідь, щоб також показати os.systemсинтаксис. Хоча це лише для ілюстрації. Дотримуйтесь підпроцесу
jdi

2
Немає конкретної версії 2.7: stackoverflow.com/questions/5495078/…, яка дозволяє ідеальне subprocess.DEVNULрішення.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Відповіді:


426

Перенаправити вихід на DEVNULL:

import os
import subprocess

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

Це фактично те саме, що виконувати цю команду оболонки:

retcode = os.system("echo 'foo' &> /dev/null")

50
мікро акуратні вибори: ви можете використовувати, os.devnullякщо subprocess.DEVNULLвін недоступний (<3.3), використовуйте check_call()замість того, call()якщо ви не перевіряєте його повернений код, відкриваєте файли у двійковому режимі stdin/stdout/stderr, використання не os.system()повинно перешкоджати, &>не працює shна Ubuntu і явний >/dev/null 2>&1може бути використаний.
jfs

1
@JFSebastian: Дякую за пропозиції. Я насправді мав намір використовувати, os.devnullале випадково набрав це. Крім того, я дотримуюся використання ОП, callоскільки вони не вловлюють можливий виняток, що check_callможе підняти. А для os.systemпереадресації це було лише просто ілюстрацією того, що робить ефективне використання підпроцесового підходу. Не дуже, як друга пропозиція.
jdi

13
Чи не потрібно закривати FNULL, який ви відкрили?
Вал

13
Просто зверніть увагу, ви можете використовувати close_fds=Trueв , subprocess.callщоб закрити FNULLдескриптор після подпроцесса існує
ewino

7
@ewino: Увімкнено close_fds=True, дескриптори файлів закриваються після, fork() але раніше, execvp() тобто закриваються в дочірньому процесі перед запуском виконуваного файлу. close_fds=Trueне працюватиме в Windows, якщо будь-який з потоків буде перенаправлений . close_fdsне закриває файли в батьківському процесі.
jfs

89

Ось більш портативна версія (просто для розваги, у вашому випадку це не потрібно):

#!/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
Зауважте, що це дає результат, DEVNULLякий не є повністю загальним, як той, що надається subprocess; оскільки він відкритий, wbйого не можна використовувати stdin.
Рейд

1
@Reid: ви можете використовувати 'r+b'режим, якщо вам це потрібно.
jfs

28

Використання subprocess.check_output(новий у python 2.7). Це придушить stdout і створить виняток, якщо команда не вдасться. (Він фактично повертає вміст stdout, тому ви можете використовувати це пізніше у своїй програмі, якщо хочете.) Приклад:

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

Ви також можете придушити stderr за допомогою:

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

Раніше, ніж 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

Тут ви можете придушити stderr

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)

Точніше, це повертає stdout. Що чудово, як, можливо, захочеться ним скористатися, окрім того, що можна ігнорувати його.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Я думаю, що це простіше. @StudentT Я думаю, що вам слід керувати помилками з CalledProcessError. except subprocess.CalledProcessError as eа потім скористайтеся e.codeабоe.output
Родріго Е. Принсіпі

21

Станом на Python3 вам більше не потрібно відкривати devnull, і ви можете викликати підпроцес.DEVNULL .

Ваш код буде оновлено як такий:

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

Працює! Крім того, можна поміняти місцями stderrз stdoutв коді вище (або додати в якості ще одного аргументу) для придушення виходів. "Виходи" є в назві питання, і що мене веде сюди ... можливо, банально, але думав, що варто згадати.
ThatsRightJack

-7

Чому б замість цього не скористатися command.getoutput ()?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)

а) він не відкидає вхід, він надмірно накопичує його в пам'яті; б) він розбивається, якщо textв ньому є котирування, або використовується інше кодування символів, або занадто велике для командного рядка; в) це лише Unix (на Python 2)
jfs
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.