【问题标题】:How do you pipe output from a Ruby script to 'head' without getting a broken pipe error如何将 Ruby 脚本的输出通过管道传输到“head”而不会出现管道损坏错误
【发布时间】:2011-02-20 04:34:54
【问题描述】:

我有一个看起来像这样的简单 Ruby 脚本

require 'csv'
while line = STDIN.gets
  array = CSV.parse_line(line)
  puts array[2]
end

但是当我尝试在这样的 Unix 管道中使用这个脚本时,我得到 10 行输出,然后是一个错误:

ruby lib/myscript.rb < data.csv  | head

12080450
12080451
12080517
12081046
12081048
12081050
12081051
12081052
12081054
lib/myscript.rb:4:in `write': Broken pipe - <STDOUT> (Errno::EPIPE)

有没有办法编写 Ruby 脚本以防止出现损坏的管道异常?

【问题讨论】:

    标签: ruby unix pipe


    【解决方案1】:

    我使用的技巧是将head 替换为sed -n 1,10p

    这使管道保持打开状态,因此ruby(或任何其他测试损坏管道并抱怨的程序)不会得到损坏的管道,因此不会抱怨。为行数选择所需的值。

    显然,这并不是试图修改您的 Ruby 脚本。几乎可以肯定,在 Ruby 代码中可以做到这一点。但是,即使您无法选择修改生成消息的程序,“sed 代替 head”技术仍然有效。

    【讨论】:

      【解决方案2】:

      head 在读取所有需要的数据后关闭标准输出流。您应该处理异常并停止写入标准输出。关闭标准输出后,以下代码将中止循环:

      while line = STDIN.gets
        array = CSV.parse_line(line)
        begin
          puts array[2]
        rescue Errno::EPIPE
          break
        end
      end
      

      【讨论】:

      • 如果 head 关闭了流,那么为什么$stdout.closed? 仍然返回false,为什么错误没有立即发生,而是在许多行被写入void 之后?我认为 head 实际上保持流打开,但不再从中读取,这导致缓冲区在某些时候已满,从而导致断点。
      • @sepp2k - 这里很可能发生了几件事:1,在使用管道时,stdout 的默认缓冲模式从面向行变为面向块,所以你需要在每次写入之间刷新。 2、head 需要有机会运行才能关闭流,但是在它有机会运行之前可能已经写入了更多字节的数据。我在每个write 之间写了一个带有$stdout.flush ; sleep 0.1 的脚本变体,在这种情况下$stdout.closed? 有效。
      • @AidanCully:啊,很有趣。
      猜你喜欢
      • 2017-01-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多