代码之家  ›  专栏  ›  技术社区  ›  porton

在Python中是BrokenPipeError,但在Perl中不是

  •  2
  • porton  · 技术社区  · 7 年前

    考虑以下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 不会产生错误(在PerlV5.26.1中)。

    2 回复  |  直到 7 年前
        1
  •  9
  •   melpomene    7 年前

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

    SIGPIPE 要发送到写入进程的信号。默认情况下,这会终止进程。这个过程可以忽略信号,如果它想,这只是使 write 呼叫失败 EPIPE

    Starting with version 3.3 ,Python提出 BrokenPipeError 在本例中出现异常,因此看起来像是python1)忽略了 信号管 EPIPE公司 断开管道错误 例外。

    默认情况下,Perl不会忽略或处理信号。这意味着它会被 信号管 在您的示例中,但由于它不是管道中的最后一个命令(这将是 在这里),shell忽略了它。不使用管道可以使其更可见:

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

    这种bash技巧使perl可以写入管道,但不能作为shell管道的一部分。我现在不能测试它,但至少可以设置 $? (命令退出状态)到 141 在外壳中(128+信号号,用于 信号管 它还可能报告 Broken pipe .

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

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

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

      STDOUT->autoflush(1) )当输出到管道或文件时,Perl将首先收集内部缓冲区中的文本(和 print 呼叫将成功)。只有当缓冲区满了(或者filehandle关闭了,以先发生的为准)时,文本才会被实际写出,缓冲区才会被清空。这就是为什么 close

        2
  •  5
  •   zdim    7 年前

    从读取过程开始引发python异常( head )关闭其结尾以便脚本接收 SIGPIPE 下次尝试写入时;请参阅 this post

    这在Perl中是看不到的,因为它被那个信号杀死(它的处置方式是什么),什么也没说。所以你可以忽略它

    use warnings;
    
    $| = 1;
    
    $SIG{PIPE} = sub { die $! };
    
    for my $i (0..4_000-1) {
        print $i, "\n";
    }
    

    (没有 $| = 1 我需要的不仅仅是 5_000 上面提到的。)

    或者,更确切地说是一个问题 warn ing(代替 die )这样程序就可以继续了

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

    更新 local 一个特殊的范围。再说,在一个大家庭里生存也没什么错 信号管 而不是被终止,只要有警告。

    请注意,即使没有这一点,Perl进程的退出状态也会显示问题。跑 echo $? 32 在我的系统里。

    为了进一步模拟Python的行为,可以发出 死亡 eval .

    melpomene ikegami 征求意见。