Python函数运行的可执行文件的终端输出如何以一般方式静音?
问题描述
我想禁止运行可执行文件的函数产生的所有终端输出.
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)
相关文章