Cara menyembunyikan output dari subproses di Python 2.7

283

Saya menggunakan eSpeak di Ubuntu dan memiliki skrip Python 2.7 yang mencetak dan mengucapkan pesan:

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

eSpeak menghasilkan suara yang diinginkan, tetapi mengacaukan shell dengan beberapa kesalahan (ALSA lib ..., tidak ada soket terhubung) sehingga saya tidak dapat dengan mudah membaca apa yang dicetak sebelumnya. Kode keluar adalah 0.

Sayangnya tidak ada opsi yang terdokumentasi untuk mematikan verbositasnya, jadi saya mencari cara untuk hanya membungkamnya secara visual dan menjaga shell terbuka bersih untuk interaksi lebih lanjut.

Bagaimana saya bisa melakukan ini?

rypel
sumber
tidak bisakah kamu menelepon dengan sistem os saja? tidak ideal tetapi tidak boleh mencetak saya tidak berpikir
Joran Beasley
@JoranBeasley: os.system () akan mencetak ke konsol kecuali Anda mengarahkan perintah shell
jdi
no, os.system ('espeak' + text) mereproduksi perilaku ini.
rypel
@ferkulat: Saya memperbarui jawaban saya untuk juga menunjukkan os.systemsintaks. Padahal itu hanya untuk ilustrasi. Tetap dengan subproses
jdi
2
Non 2.7 versi spesifik: stackoverflow.com/questions/5495078/… yang memungkinkan untuk subprocess.DEVNULsolusi sempurna .
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功

Jawaban:

426

Redirect output ke DEVNULL:

import os
import subprocess

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

Secara efektif sama dengan menjalankan perintah shell ini:

retcode = os.system("echo 'foo' &> /dev/null")
jdi
sumber
50
picks mikro rapi: Anda dapat menggunakan os.devnulljika subprocess.DEVNULLtidak tersedia (<3.3), gunakan check_call()alih-alih call()jika Anda tidak memeriksa kode yang dikembalikan, buka file dalam mode biner untuk stdin/stdout/stderr, penggunaan os.system()harus tidak disarankan, &>tidak berfungsi untuk shpada Ubuntu dan eksplisit >/dev/null 2>&1bisa digunakan.
jfs
1
@ JSFSebastian: Terima kasih atas sarannya. Saya sebenarnya bermaksud menggunakan os.devnulltetapi tidak sengaja mengetiknya. Juga, saya tetap menggunakan OP callkarena mereka tidak menangkap kemungkinan pengecualian check_callakan meningkat. Dan untuk os.systempengalihan, itu lebih merupakan ilustrasi tentang apa yang dilakukan dengan efektif penggunaan pendekatan subproses. Bukan sebagai saran kedua.
jdi
13
Apakah Anda tidak perlu menutup FNULL yang telah Anda buka?
Val
13
Hanya catatan, Anda dapat menggunakan close_fds=Truedi subprocess.calluntuk menutup FNULLdeskriptor setelah sub proses yang ada
ewino
7
@ewino: Aktif close_fds=True, deskriptor file ditutup setelah fork() tetapi sebelum execvp() mis., mereka ditutup dalam proses anak sebelum dieksekusi dijalankan. close_fds=Truetidak akan berfungsi di Windows jika ada aliran dialihkan . close_fdstidak menutup file dalam proses induk .
jfs
89

Ini versi yang lebih portabel (hanya untuk bersenang-senang, tidak perlu dalam kasus Anda):

#!/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
sumber
4
Perhatikan bahwa ini menghasilkan DEVNULLyang tidak sepenuhnya umum, seperti yang disediakan oleh subprocess; sejak dibuka wbitu tidak dapat digunakan untuk stdin.
Reid
1
@ Reid: Anda bisa menggunakan 'r+b'mode jika Anda membutuhkannya.
jfs
28

Gunakan subprocess.check_output(baru dalam python 2.7). Ini akan menekan stdout dan memunculkan eksepsi jika perintah gagal. (Ini sebenarnya mengembalikan konten stdout, sehingga Anda dapat menggunakannya nanti di program Anda jika Anda mau.) Contoh:

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

Anda juga dapat menekan stderr dengan:

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

Untuk lebih awal dari 2,7, gunakan

import os
import subprocess
with open(os.devnull, 'w')  as FNULL:
    try:
        subprocess._check_call(['espeak', text], stdout=FNULL)
    except subprocess.CalledProcessError:
        # Do something

Di sini, Anda dapat menekan stderr dengan

        subprocess._check_call(['espeak', text], stdout=FNULL, stderr=FNULL)
Zags
sumber
Lebih tepatnya, ia mengembalikan stdout. Yang bagus mungkin ingin menggunakannya juga selain bisa mengabaikannya.
Ciro Santilli 郝海东 冠状 病 六四 事件 法轮功
Saya pikir ini lebih mudah. @StudentT Saya pikir Anda harus menangani kesalahan dengan CalledProcessError. except subprocess.CalledProcessError as edan kemudian gunakan e.codeataue.output
Rodrigo E. Principe
21

Pada Python3 Anda tidak perlu lagi membuka devnull dan dapat memanggil subprocess.DEVNULL .

Kode Anda akan diperbarui seperti itu:

import subprocess
text = 'Hello World.'
print(text)
subprocess.call(['espeak', text], stderr=subprocess.DEVNULL)
Josh Correia
sumber
Bekerja! Selain itu, dapat bertukar stderrdengan stdoutkode di atas (atau menambahkan argumen lain) untuk menekan output. "Keluaran" ada dalam judul pertanyaan dan apa yang menuntun saya ke sini ... mungkin sepele, tetapi berpikir itu layak disebut.
ThatsRightJack
-7

Mengapa tidak menggunakan command.getoutput () saja?

import commands

text = "Mario Balotelli" 
output = 'espeak "%s"' % text
print text
a = commands.getoutput(output)
lolamontes69
sumber
a) tidak membuang input, mengakumulasikannya dalam memori yang tidak perlu b) rusak jika textmemiliki tanda kutip di dalamnya, atau menggunakan pengkodean karakter yang berbeda, atau terlalu besar untuk baris perintah c) itu hanya Unix (pada Python 2)
jfs