问题描述
我想禁止运行可执行文件的函数产生的所有终端输出.
I want to suppress all of the terminal output produced by a function that runs executables.
我试图通过使用上下文管理器来抑制 Python 函数的输出,该上下文管理器在每次调用函数时都会临时重新定义 stdout 和 stderr.这会抑制函数中 print 调用产生的终端输出,但当函数调用产生终端输出的可执行文件时,它似乎不起作用.
I have attempted to suppress the output of a Python function by using a context manager that temporarily redefines stdout and stderr each time the function is called. This suppresses terminal output produced by print calls in the function, but it doesn't seem to work when the function calls executables that produce terminal output.
那么,如何抑制 Python 函数调用的可执行文件的输出呢?
So, how could the output of executables called by Python functions be suppressed?
我的代码如下.我已经包含了一个示例函数,它调用 ls 来尝试说明我想要抑制的终端输出类型(尽管我正在处理的函数不同).
My code is below. I have included an example function that calls ls to try to illustrate the kind of terminal output I want to suppress (though the function I'm dealing with is different).
#!/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 函数):
In my particular case, I'm trying to run the following function (instead of the ls function above):
with propyte.silence(): stream = pyaudio.PyAudio().open( format = pyaudio.PyAudio().get_format_from_width(1), channels = 1, rate = bitrate, output = True )
运行时,会产生如下输出:
When run, this produces output like the following:
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 提供的解决方案
testing a solution provided by @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 的终端输出,我不知道为什么.
I have not been successful in suppressing the terminal output from the print or the ls and I'm not sure why.
推荐答案
你可以从 PyAudio 切换到 sounddevice 模块,它已经负责使终端输出静音(参见 #12).这是在那里完成的(使用 CFFI):
You could switch from PyAudio to the sounddevice module, which already takes care of silencing the terminal output (see #12). This is how it is done there (using 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 的解决方案,你可以试试这个上下文管理器:
If you want a pure Python solution, you can try this context manager:
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/.
This is a very helpful blog post about the topic: http://eli.thegreenplace.net/2015/redirecting-all-kinds-of-stdout-in-python/.
更新:
上面的上下文管理器使标准错误输出 (stderr) 静音,该输出用于原始问题中提到的来自 PortAudio 的烦人消息.要摆脱标准输出(stdout),就像您更新的问题一样,您必须将 sys.stderr 替换为 sys.stdout 和文件描述符 2 与数字 1:
The context manager above silences the standard error output (stderr), which is used for the annoying messages from PortAudio mentioned in the original question. To get rid of the standard output (stdout), as in your updated question, you'll have to replace sys.stderr with sys.stdout and the file descriptor 2 with the number 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)