我正在尝试在python中调用shell脚本,但它一直报告管道错误(结果没问题,但我不想在STDERR中看到错误消息)。我已经确定了原因,可以将其复制为以下片段:
subprocess.call('cat /dev/zero | head -c 10 | base64', shell=True)
AAAAAAAAAAAAAA ==
cat:写入错误:管道损坏
/dev/zero
是一个无限流,但是head -c 10
只从它读取10个字节并退出,然后cat将获得SIGPIPE,因为对等体已关闭管道。当我在shell中运行命令时,没有损坏的管道错误消息,但为什么python会显示它?
答案 0 :(得分:8)
SIGPIPE信号的默认操作是终止程序。 Python解释器将其更改为SIG_IGN,以便能够以异常的形式向程序报告损坏的管道错误。
在shell中执行cat ... |head ...
时,cat
具有默认的SIGPIPE处理程序,而OS内核只是在SIGPIPE上终止它。
使用cat
执行subprocess
时,它从其父(python解释器)派生SIGPIPE处理程序,SIGPIPE被忽略,cat
通过检查errno
来处理错误本身变量和打印错误消息。
为避免来自cat
的错误消息,您可以将preexec_fn
参数用于subprocess.call:
from signal import signal, SIGPIPE, SIG_DFL
subprocess.call(
'cat /dev/zero | head -c 10 | base64',
shell = True,
preexec_fn = lambda: signal(SIGPIPE, SIG_DFL)
)
答案 1 :(得分:2)
在这个微不足道的案例中,至少你没有通过使用shell命令获得任何东西 - 而且你正在失去可移植性和速度。
Python 2代码:
>>> import base64
>>> base64.b64encode(open('/dev/zero', 'rb').read(10))
'AAAAAAAAAAAAAA=='
>>> base64.b64encode('\0' * 10)
'AAAAAAAAAAAAAA=='
在Python 3中(代码也将在2.6+中运行,但它将返回str
而不是bytes
个实例):
>>> import base64
>>> base64.b64encode(open('/dev/zero', 'rb').read(10))
b'AAAAAAAAAAAAAA=='
>>> base64.b64encode(b'\0' * 10)
b'AAAAAAAAAAAAAA=='
在每种情况下,第一个例子保留/dev/zero
的使用(本身是非便携式,但没关系),第二个例子产生效果,虽然我想它不是你想要的具体?