Python 中的 BrokenPipeError 但 Perl 中没有

BrokenPipeError in Python but not in Perl

考虑这个 Python 脚本:

for i in range(4000):
    print(i)

和这个 Perl 脚本:

for my $i (0..4000-1) {
    print $i, "\n";
}

python3 pipe.py | head -n3000 >/dev/null 产生错误:

Exception ignored in: <_io.TextIOWrapper name='<stdout>' mode='w' encoding='UTF-8'>
BrokenPipeError: [Errno 32] Broken pipe

但是

perl pipe.pl | head -n3000 >/dev/null 不产生错误(在 Perl v5.26.1 中)。

为什么 Python 和 Perl 之间存在如此大的差异?如何让 Perl 产生类似的错误信息?

由于读取进程 (head) 结束而引发 python 异常,因此脚本在下次尝试写入时收到 SIGPIPE;参见 this post。这涉及 Python 社区的决定,更改默认值以通知用户(参见链接 post)。

这在 Perl 中是看不到的,因为它被那个信号(它的倾向是什么)杀死而什么也没说。所以你可以覆盖那个

use warnings;

$| = 1;

$SIG{PIPE} = sub { die $! };

for my $i (0..4_000-1) {
    print $i, "\n";
}

(没有 $| = 1 我需要超过 5_000 才能实现。)

或者,发出 warning(而不是 die)以便程序继续

local $SIG{PIPE} = sub { warn "Ignoring $_[0]: $!" };

更新 考虑到评论中提供的说明,我建议该处理程序实际上是全局的。它仍然可以在特定范围内用 local 覆盖。再说了,生存SIGPIPE而不是被终止也没有错,只要有警告

请注意,即使没有它,Perl 进程的退出状态也会显示问题。 运行 echo $? 在进程 "completes" 之后(安静地终止);我的系统上显示 32

为了进一步模仿 Python 的行为,您可以在信号处理程序中发出 die 并处理该异常,方法是将其全部放入 eval.

感谢 melpomene and ikegami 的评论。

这里发生的事情是,在这两种情况下,您都有一个进程写入一个读取端已关闭的管道(通过 head 在一定数量的字节后退出)。

这会导致 SIGPIPE 信号被发送到写入进程。默认情况下,这会终止进程。该进程可以根据需要忽略该信号,这只会使 write 调用失败并出现 EPIPE 错误。

在这种情况下,

Starting with version 3.3、Python 引发了一个 BrokenPipeError 异常,因此看起来 Python 1) 默认忽略 SIGPIPE 和 2)将 EPIPE 转换为 BrokenPipeError 异常。

默认情况下,Perl 不会忽略或处理信号。这意味着它在您的示例中被 SIGPIPE 杀死,但因为它不是管道中的最后一个命令(此处为 head),shell 只是忽略它。您可以通过不使用管道使其更加可见:

perl pipe.pl > >(head -n3000 >/dev/null)

这段 bash 技巧使 perl 写入管道,但不是 shell 管道的一部分。我现在无法测试它,但至少这会将 $?(命令退出状态)设置为 shell 中的 141(128 + 信号编号,对于 SIGPIPE 是 13),它也可能报告 Broken pipe.

不过您可以在 Perl 代码中手动处理它:

  • 变体 1:从信号处理程序中抛出错误

    $SIG{PIPE} = sub { die "BrokenPipeError" };
    
  • 变体 2:忽略信号,处理写入错误

    $SIG{PIPE} = 'IGNORE';
    ...
    print $i, "\n" or die "Can't print: $!";
    

    请注意,在这种情况下,您必须考虑缓冲。如果你不启用自动刷新(如在 STDOUT->autoflush(1) 中)并且输出将进入管道或文件,Perl 将首先在内部缓冲区中收集文本(并且 print 调用将成功)。只有当缓冲区变满(或文件句柄关闭,以先发生者为准)时,文本才真正写出并且缓冲区被清空。这也是为什么close也能报写错误的原因