Как скрыть вывод подпроцесса в Python 2.7

283

Я использую eSpeak в Ubuntu и у меня есть скрипт Python 2.7, который печатает и произносит сообщение:

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

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

К сожалению, нет задокументированной опции для отключения его многословия, поэтому я ищу способ только визуально заставить его замолчать и сохранить открытую оболочку в чистоте для дальнейшего взаимодействия.

Как я могу это сделать?

rypel
источник
не могли бы вы тогда просто позвонить с os.system? не идеально, но не должен печатать, я не думаю
Джоран Бизли
@JoranBeasley: os.system () будет выводить на консоль, если вы не перенаправите команду оболочки
jdi
нет, os.system ('espeak' + text) воспроизводит это поведение.
rypel
@ferkulat: я обновил свой ответ, чтобы также показать os.systemсинтаксис. Хотя это только для иллюстрации. Палка с подпроцессом
JDI
2
Не для 2.7 конкретной версии: stackoverflow.com/questions/5495078/…, которая позволяет найти идеальное subprocess.DEVNULрешение.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Ответы:

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")
JDI
источник
50
микро аккуратные выборки: вы можете использовать, os.devnullесли subprocess.DEVNULLнедоступен (<3.3), использовать check_call()вместо, call()если вы не проверяете возвращаемый код, открывайте файлы в двоичном режиме stdin/stdout/stderr, использование os.system()не рекомендуется, &>для shUbuntu явное >/dev/null 2>&1может быть использовано.
Jfs
1
@JFSebastian: Спасибо за предложения. Я действительно хотел использовать, os.devnullно случайно набрал его. Кроме того, я придерживаюсь использования OP, callпоскольку они не улавливают возможное исключение check_call. И для os.systemперенаправления это было больше просто иллюстрацией того, что делает эффективное использование подхода подпроцесса. Не совсем как второе предложение.
jdi
13
Вам не нужно закрывать FNULL, который вы открыли?
Вэл
13
Просто обратите внимание, вы можете использовать close_fds=Trueв , subprocess.callчтобы закрыть FNULLдескриптор после подпроцесса существует
ewino
7
@ewino: On 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
JFS
источник
4
Обратите внимание, что это приводит к тому, DEVNULLчто он не является полностью общим, как тот, который предоставляется subprocess; так как он открыт, wbон не может быть использован для stdin.
Рейд
1
@Reid: вы можете использовать 'r+b'режим, если он вам нужен.
JFS
28

Использование subprocess.check_output(новое в Python 2.7). Это подавит стандартный вывод и вызовет исключение в случае сбоя команды. (На самом деле он возвращает содержимое стандартного вывода, поэтому вы можете использовать его позже в своей программе, если хотите.) Пример:

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)
Zags
источник
Точнее, он возвращает стандартный вывод. Это здорово, так как вы можете захотеть использовать его, кроме возможности игнорировать его.
Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功
Я думаю, что это более просто. @StudentT Я думаю, что вы должны обрабатывать ошибки с CalledProcessError. except subprocess.CalledProcessError as eа затем использовать e.codeилиe.output
Родриго Е. Принсипи
21

Начиная с Python3 вам больше не нужно открывать devnull и вы можете вызывать subprocess.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)
lolamontes69
источник
а) он не сбрасывает ввод, он накапливает его в памяти без необходимости б) он ломается, если в нем textесть кавычки, или использует другую кодировку символов, или слишком велик для командной строки в) он только для Unix (на Python 2)
JFS