วิธีซ่อนเอาต์พุตของกระบวนการย่อยใน 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' + ข้อความ) จะทำซ้ำพฤติกรรมนี้
rypel

@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แต่พิมพ์ออกมาโดยไม่ตั้งใจ นอกจากนี้ฉันยังคงใช้ OPs callเนื่องจากพวกเขาไม่ได้จับข้อยกเว้นที่เป็นไปได้check_callจะเพิ่ม และสำหรับการos.systemเปลี่ยนเส้นทางมันเป็นเพียงภาพประกอบที่แสดงให้เห็นว่าการใช้วิธีการ subprocess อย่างมีประสิทธิภาพนั้นเป็นอย่างไร ไม่เป็นข้อเสนอแนะที่สองจริงๆ
jdi

13
คุณไม่จำเป็นต้องปิด FNULL ที่คุณเปิดใช่ไหม
Val

13
เพียงทราบคุณสามารถใช้close_fds=Trueในsubprocess.callการปิดFNULLdescriptor หลังจากกระบวนการย่อยที่มีอยู่
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ได้
Reid

1
@ เร่: คุณสามารถใช้'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 อีกต่อไปและสามารถเรียกsubprocess.DEVNULLได้

รหัสของคุณจะได้รับการอัปเดตดังนี้:

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

Works! นอกจากนี้ยังสามารถสลับ stderrกับstdoutในรหัสข้างต้น (หรือผนวกเป็นอาร์กิวเมนต์อื่น) เพื่อระงับการแสดงผล "เอาท์พุท" อยู่ในชื่อของคำถามและสิ่งที่ทำให้ฉันที่นี่ ... อาจจะเล็กน้อย แต่คิดว่ามันคุ้มค่าที่จะกล่าวถึง
ThatsRightJack

-7

ทำไมไม่ใช้ command.getoutput () แทน?

import commands

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

a) มันไม่ทิ้งอินพุตมันสะสมในหน่วยความจำโดยไม่จำเป็น b) มันจะแตกถ้าtextมีเครื่องหมายคำพูดอยู่หรือใช้การเข้ารหัสอักขระที่แตกต่างกันหรือใหญ่เกินไปสำหรับบรรทัดคำสั่ง c) เป็น Unix เท่านั้น (บน Python 2)
jfs
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.