Как скрыть вывод подпроцесса в 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? не идеально, но не должен печатать, я не думаю
— Джоран Бизли

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

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

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)

Точнее, он возвращает стандартный вывод. Это здорово, так как вы можете захотеть использовать его, кроме возможности игнорировать его.
— Сиро Сантилли 郝海东 冠状 病 六四 事件 法轮功

Я думаю, что это более просто. @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)

а) он не сбрасывает ввод, он накапливает его в памяти без необходимости б) он ломается, если в нем textесть кавычки, или использует другую кодировку символов, или слишком велик для командной строки в) он только для Unix (на Python 2)
— JFS
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.