8

我试图在 python 中调用一个 shell 脚本,但它一直在报告损坏的管道错误(结果是好的,但我不想在 STDERR 中看到错误消息)。我已经查明了原因,并且可以将其复制为以下代码段:

subprocess.call('cat /dev/zero | head -c 10 | base64', shell=True)

啊啊啊啊啊啊==

猫:写错误:断管

/dev/zero是一个无限流,但head -c 10只有从它读取 10 个字节并退出,然后 cat 将获得 SIGPIPE 因为对等方已关闭管道。当我在 shell 中运行命令时没有损坏管道错误消息,但为什么 python 显示它?

4

2 回答 2

9

SIGPIPE 信号的默认操作是终止程序。Python 解释器将其更改为 SIG_IGN 以便能够以异常的形式向程序报告损坏的管道错误。

当您cat ... |head ...在 shell 中执行时,cat具有默认的 SIGPIPE 处理程序,并且操作系统内核只是在 SIGPIPE 上终止它。

当您cat使用subprocess它从其父级(python 解释器)派生 SIGPIPE 处理程序执行时,SIGPIPE 只是被忽略并通过检查变量和打印错误消息cat来处理错误本身。errno

为避免来自您的错误消息,cat您可以使用preexec_fnsubprocess.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)
)
于 2012-09-16T21:58:33.810 回答
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(本身不可移植,但没关系)的用法,第二个产生了效果,尽管我想这不是您想要的?

于 2012-05-07T10:30:02.650 回答