Ruby STDIN,阻塞vs不阻塞

时间:2015-07-29 01:16:00

标签: ruby shell pipe stdin

我试图找到一些关于如何在Ruby中处理STDIN的文档。

我已尝试过这个简单的脚本:

# test.rb

loop do
  puts "stdin: #{$stdin.gets}"
  sleep 2
end

我已经从 bash (在OS X上)运行:

$ ruby test.rb

正如我所料,对$stdin.gets的调用是阻塞的,循环等待下一个输入。 2秒的睡眠时间甚至允许我一次输入更多行,并且循环正确地按顺序打印它们,然后在清除STDIN时再次停止:

$ ruby test.rb
a
stdin: a
b
stdin: b
c
d
e
stdin: c
stdin: d
stdin: e
到目前为止,一切都很好。我在期待这个。

然后,我用管道进行了测试:

$ mkfifo my_pipe
$ ruby test.rb < my_pipe

而且,在另一个shell中:

$ echo "Hello" > my_pipe

这一次,它表现得有点不同 起初它确实等待,阻止循环。但是,在第一个输入通过管道后,它会保持循环并打印空字符串:

$ ruby test.rb
stdin: Hello
stdin:
stdin:
stdin: Other input
stdin:

所以我的问题是:为什么差异?它将管道视为空文件吗?这记录在哪里? docs并未对阻止行为发表任何言论,但他们确实说过:

  

如果在文件末尾调用,则返回nil。

这是一个开始。

1 个答案:

答案 0 :(得分:1)

所以简短的回答是肯定的,你从管道中获得了一个EOF。因为echo的工作方式是它打开管道进行写入,写入,然后关闭(即发送EOF)。然后,对echo的新调用将打开它,读取它,然后关闭。

如果您在3秒睡眠后使用了打印文件行的程序,您会看到您的应用程序将执行阻塞等待直到退出(此时永不停止的EOF将返回)。 / p>

# slow_write.rb
ARGF.each do |line|
  puts line
  STDOUT.flush
  sleep 3
end

我应该注意,这种行为并不特定于Ruby。 C stdlio库具有完全相同的行为,并且由于大多数语言使用C原语作为基础,因此它们也具有相同的行为。