Python 函数的可执行文件 运行 的终端输出如何以一般方式静音?
How can the terminal output of executables run by Python functions be silenced in a general way?
我想抑制 运行 可执行文件的函数产生的所有终端输出。
我试图通过使用上下文管理器来抑制 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
的终端输出,我不确定为什么。
特别是对于 ALSA 错误,您可以使用 ALSA 的 snd_lib_error_set_handler
函数,例如 in this question.
对于我的一个项目,我创建了一个名为 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
从更一般的意义上讲,您可以阻止 任何东西 打印到
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')
这种方法的缺点是它会使 所有 消息静音
stderr
,这意味着您将不再看到合法错误
消息。这可能会导致令人惊讶的故障模式(程序
没有打印任何错误就退出了!)。
您可以从 PyAudio 切换到 sounddevice module, which already takes care of silencing the terminal output (see #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)
这是一个非常有用的博客post,主题是: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)
下午好,为了解决错误,只需添加以下解释:
导入声音设备
如果我需要安装:
python3 -m pip install sounddevice
我想抑制 运行 可执行文件的函数产生的所有终端输出。
我试图通过使用上下文管理器来抑制 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
我想抑制那个输出。
编辑:测试
#!/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
的终端输出,我不确定为什么。
特别是对于 ALSA 错误,您可以使用 ALSA 的 snd_lib_error_set_handler
函数,例如 in this question.
对于我的一个项目,我创建了一个名为 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
从更一般的意义上讲,您可以阻止 任何东西 打印到
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')
这种方法的缺点是它会使 所有 消息静音
stderr
,这意味着您将不再看到合法错误
消息。这可能会导致令人惊讶的故障模式(程序
没有打印任何错误就退出了!)。
您可以从 PyAudio 切换到 sounddevice module, which already takes care of silencing the terminal output (see #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)
这是一个非常有用的博客post,主题是: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)
下午好,为了解决错误,只需添加以下解释:
导入声音设备
如果我需要安装:
python3 -m pip install sounddevice