Ruby管道、IO和标准错误重定向

4

我希望能够编写一个 Ruby 程序 (一个 Rake 任务),观察另一个 Rake 任务的输出。这个输出写入到 stderr,我想读取这些行。但我在设置时遇到了困难。如果我有一个写入器 (stdout_writer.rb) 不停地打印一些内容:

#!/usr/bin/env ruby
puts 'writing...'
while true
  $stdout.puts '~'
  sleep 1
end

还有一个读取并回显的文件(stdin_reader.rb):

#!/usr/bin/env ruby
puts 'reading...'
while input = ARGF.gets
  puts input
  input.each_line do |line|
    begin
      $stdout.puts "got line #{line}"
    rescue Errno::EPIPE
      exit(74)
    end
  end
end

而我正试图让它们一起工作,但什么也没有发生:

$ ./stdout_writer.rb 2>&1 | ./stdin_reader.rb
$ ./stdout_writer.rb | ./stdin_reader.rb

什么也没有……但是如果我只是在stdin_reader.rb中输出,我会得到期望的结果:

piousbox@e7440:~/projects/opera_events/sendgrid-example-operaevent$ echo "ok true" | ./stdin_reader.rb
reading...
ok true
got line ok true
piousbox@e7440:~/projects/opera_events/sendgrid-example-operaevent$ 

如何设置一个脚本,让它能够将stderr输入,以便可以逐行读取?其他信息:这将是一个Ubuntu的Upstart服务 script1.rb | script2.rb其中script1发送一条消息,而script2验证该消息是否由script1发送。

2个回答

1
问题似乎在于,由于stdout_writer无限运行,stdin_reader永远没有机会从stdout_writer读取STDOUT,因为在这种情况下,管道正在等待stdout_writer完成后才开始读取stdin_reader。我通过将while true更改为5.times do来进行了测试。如果您这样做,并等待5秒钟,则./stdout_writer.rb | ./stdin_reader.rb的结果为

reading...
writing...
got line writing...
~
got line ~
~
got line ~
~
got line ~
~
got line ~
~
got line ~

这并不是你的代码本身出现了问题,而是关于 ruby 执行过程中 STDOUT | STDIN 处理方式的问题。
此外,我认为我从研究这个问题中学到了很多。感谢这次有趣的练习。

1
< p >来自 stdout_writer.rb 的输出被 Ruby 缓冲,因此读取进程无法看到它。如果等待足够长的时间,您应该会看到结果分块出现。

您可以在 stdout_writer.rb 开始时将 $stdout 的同步设置为 true,关闭缓冲并获得您期望的结果:

$stdout.sync = true 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接