Python函数运行的可执行文件的终端输出如何通用静音?

问题描述 投票:0回答:5

我想抑制运行可执行文件的函数产生的所有终端输出。

我试图通过使用上下文管理器来抑制 Python 函数的输出,该上下文管理器在每次调用函数时临时重新定义 stdout 和 stderr。这抑制了函数中

print
调用产生的终端输出,但当函数调用产生终端输出的可执行文件时它似乎不起作用。

那么,如何抑制 Python 函数调用的可执行文件的输出?

我的代码如下。我已经包含了一个示例函数,它调用

ls
来尝试说明我想要抑制的终端输出类型(尽管我正在处理的函数是不同的)。

#!/usr/bin/env python

import os
import subprocess
import sys

def main():

    print("hello")

    with silence():
        print("there")

    print("world")

    with silence():
        engage_command(command = "ls")

class silence(object):

    def __init__(
        self,
        stdout = None,
        stderr = None
        ):
        if stdout == None and stderr == None:
            devnull = open(os.devnull, "w")
            stdout = devnull
            stderr = devnull
        self._stdout = stdout or sys.stdout
        self._stderr = stderr or sys.stderr

    def __enter__(
        self
        ):
        self.old_stdout = sys.stdout
        self.old_stderr = sys.stderr
        self.old_stdout.flush()
        self.old_stderr.flush()
        sys.stdout = self._stdout
        sys.stderr = self._stderr

    def __exit__(
        self,
        exc_type,
        exc_value,
        traceback
        ):
        self._stdout.flush()
        self._stderr.flush()
        sys.stdout = self.old_stdout
        sys.stderr = self.old_stderr

def engage_command(
    command = None
    ):
    process = subprocess.Popen(
        [command],
        shell      = True,
        executable = "/bin/bash")
    process.wait()
    output, errors = process.communicate()
    return output

if __name__ == "__main__":
    main()

在我的特殊情况下,我正在尝试运行以下函数(而不是上面的

ls
函数):

with propyte.silence():
    stream = pyaudio.PyAudio().open(
        format   = pyaudio.PyAudio().get_format_from_width(1),
        channels = 1, 
        rate     = bitrate, 
        output   = True
    )

运行时,会产生如下输出:

ALSA lib pcm_dsnoop.c:606:(snd_pcm_dsnoop_open) unable to open slave
ALSA lib pcm_dmix.c:1029:(snd_pcm_dmix_open) unable to open slave
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2266:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side
ALSA lib pcm_dmix.c:1029:(snd_pcm_dmix_open) unable to open slave
Cannot connect to server socket err = No such file or directory
Cannot connect to server request channel
jack server is not running or cannot be started
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock
JackShmReadWritePtr::~JackShmReadWritePtr - Init not done for 4294967295, skipping unlock

我想抑制那个输出。


编辑:测试@Matthias提供的解决方案

#!/usr/bin/env python

import contextlib
import os
import subprocess
import sys

def main():

    print("hello")

    with silence():
        print("there")

    print("world")

    with silence():
        engage_command(command = "ls")

@contextlib.contextmanager
def silence():
    devnull = os.open(os.devnull, os.O_WRONLY)
    old_stderr = os.dup(2)
    sys.stderr.flush()
    os.dup2(devnull, 2)
    os.close(devnull)
    try:
        yield
    finally:
        os.dup2(old_stderr, 2)
        os.close(old_stderr)

def engage_command(
    command = None
    ):
    process = subprocess.Popen(
        [command],
        shell      = True,
        executable = "/bin/bash")
    process.wait()
    output, errors = process.communicate()
    return output

if __name__ == "__main__":
    main()

我没有成功抑制

print
ls
的终端输出,我不确定为什么。

python console executable pyaudio silent
5个回答
6
投票

特别是对于 ALSA 错误,您可以使用 ALSA 的

snd_lib_error_set_handler
功能,如本问题中所述的示例

对于我的一个项目,我创建了一个名为

mute_alsa
的模块,它看起来像这样:

import ctypes

ERROR_HANDLER_FUNC = ctypes.CFUNCTYPE(None, ctypes.c_char_p, ctypes.c_int,
                                      ctypes.c_char_p, ctypes.c_int,
                                      ctypes.c_char_p)


def py_error_handler(filename, line, function, err, fmt):
    pass

c_error_handler = ERROR_HANDLER_FUNC(py_error_handler)

try:
    asound = ctypes.cdll.LoadLibrary('libasound.so.2')
    asound.snd_lib_error_set_handler(c_error_handler)
except OSError:
    pass

只需将以下代码放入代码中即可使用它:

import mute_alsa

在更一般的意义上,您可以防止anything打印到

stderr
只需关闭关联的文件描述符即可。为了 例如,如果我们尝试
rm
一个不存在的文件,我们会得到一个错误 消息打印到
stderr

>>> import sys
>>> import os
>>> os.system('rm /doesnotexist')
rm: cannot remove ‘/doesnotexist’: Is a directory
256

如果我们关闭

stderr
文件描述符:

>>> os.close(sys.stderr.fileno())

我们不再看到任何错误消息:

>>> os.system('rm /doesnotexist')

这种方法的缺点是它会使all消息静音

stderr
,这意味着您将不再看到合法错误 消息。这可能会导致令人惊讶的故障模式(程序 没有打印任何错误就退出了!)。


5
投票

您可以从 PyAudio 切换到 sounddevice 模块,它已经负责使终端输出静音(参见 #12)。这是在那里完成的(使用 CFFI):

from cffi import FFI
import os

ffi = FFI()
ffi.cdef("""
/* from stdio.h */
FILE* fopen(const char* path, const char* mode);
int fclose(FILE* fp);
FILE* stderr;  /* GNU C library */
FILE* __stderrp;  /* Mac OS X */
""")
try:
    stdio = ffi.dlopen(None)
    devnull = stdio.fopen(os.devnull.encode(), b'w')
except OSError:
    return
try:
    stdio.stderr = devnull
except KeyError:
    try:
        stdio.__stderrp = devnull
    except KeyError:
        stdio.fclose(devnull)

如果你想要一个纯 Python 解决方案,你可以试试这个上下文管理器:

import contextlib
import os
import sys

@contextlib.contextmanager
def ignore_stderr():
    devnull = os.open(os.devnull, os.O_WRONLY)
    old_stderr = os.dup(2)
    sys.stderr.flush()
    os.dup2(devnull, 2)
    os.close(devnull)
    try:
        yield
    finally:
        os.dup2(old_stderr, 2)
        os.close(old_stderr)

这是一篇关于该主题的非常有用的博客文章:http://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/.


更新:

上面的上下文管理器使标准错误输出(

stderr
)静音,它用于原始问题中提到的来自 PortAudio 的恼人消息。要摆脱标准输出(
stdout
),就像在更新的问题中一样,您必须将
sys.stderr
替换为
sys.stdout
并将文件描述符
2
替换为数字
1
:

@contextlib.contextmanager
def ignore_stdout():
    devnull = os.open(os.devnull, os.O_WRONLY)
    old_stdout = os.dup(1)
    sys.stdout.flush()
    os.dup2(devnull, 1)
    os.close(devnull)
    try:
        yield
    finally:
        os.dup2(old_stdout, 1)
        os.close(old_stdout)

1
投票

下午好,解决错误,只需添加以下解释:

进口音响设备

如果我需要安装:

python3 -m pip 安装声音设备


0
投票

在不使终端静音的情况下使这些可执行文件静音的最简单方法是导入 sounddevice 模块。

pip -m install sounddevice

如果您想了解有关声音设备模型的更多详细信息,请阅读文档: https://python-sounddevice.readthedocs.io/en/0.4.6/index.html

(sounddevice)模块总结:

它“为 PortAudio 库提供绑定和一些便利 播放和记录包含音频信号的 NumPy 数组的函数” (python-sounddevice,版本 0.4.6,2019)。

我的工作代码:

import pyaudio
import wave
import sounddevice

FRAMES_PER_BUFFER = 3200
FORMAT = pyaudio.paInt16
CHANNELS = 1
RATE = 16000

p = pyaudio.PyAudio()

stream = p.open(
        format=FORMAT,
        channels=CHANNELS,
        rate=RATE,
        input=True,
        frames_per_buffer=FRAMES_PER_BUFFER
)

print("start recording")

导入前的输出:

ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.rear
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.center_lfe
ALSA lib pcm.c:2664:(snd_pcm_open_noupdate) Unknown PCM cards.pcm.side

@Rafhael P. Zrenner 谢谢!!!!由于您的快速解决方案,我可以继续使用 pyttsx3 和 SpeechRecognition 模块开发我的 AI 助手。


0
投票

正如 Rafhael P. Zrenner 所说,你只需要使用 pip 来安装 sounddevice:

pip install sounddevice
© www.soinside.com 2019 - 2024. All rights reserved.