Як приховати вихід підпроцесу в 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.