Ruby断线@ io_write-<STDOUT>

时间:2019-09-24 14:37:05

标签: ruby pipe stdout

在尝试运行ruby程序并将输出通过管道传递到另一个程序时,如下所示:

ruby hello.rb | whoami

首先按预期执行命令whoami,但是此后,hello.rb崩溃,并显示:

Traceback (most recent call last):
    2: from p.rb:2:in `<main>'
    1: from p.rb:2:in `print'
p.rb:2:in `write': Broken pipe @ io_write - <STDOUT> (Errno::EPIPE)

只有在STDOUT.sync设置为true时才会发生

STDOUT.sync = true
STDOUT.print "Hello!"

[[并且在通过管道传输到另一个程序时,STDOUT.flush之后的STDOUT.puts会引发类似的错误]

此崩溃的原因是什么?

1 个答案:

答案 0 :(得分:1)

简介

首先,可以找到说明here

无论如何,这是我的想法...

当这样使用管道时:

a | b

a和b均执行concurrently。 b等待来自a的标准输入。

谈到Errno::EPIPELinux man page of write说:

  

EPIPE fd连接到读数端为             关闭。当发生这种情况时,写作过程也将             接收SIGPIPE信号。 (因此,写入返回值为             仅在程序捕获,阻止或忽略此内容时可见             信号。)

谈论问题中的问题: 运行程序whoami时,它退出并且不再接受ruby程序hello.rb发送的标准输入-导致管道损坏。

在这里,我编写了2个红宝石程序,分别名为p.rb和q.rb进行测试:

  • p.rb
#!/usr/bin/env ruby
print ?* * 100_000
  • q.rb
#!/usr/bin/ruby
exit! 0

运行:

bash[~] $ ruby p.rb | ruby q.rb

Traceback (most recent call last):
    2: from p.rb:2:in `<main>'
    1: from p.rb:2:in `print'
p.rb:2:in `write': Broken pipe @ io_write - <STDOUT> (Errno::EPIPE)

让我们稍微更改 q.rb 的代码,以便它接受输入:

#!/usr/bin/ruby -w
STDIN.gets

运行:

bash[~] $ ruby p.rb | ruby q.rb

对,它实际上什么也不显示。原因是q.rb现在正在等待标准输入。 显然,等待是最重要的。现在,即使通过STDOUT.syncSTDOUT.flush传递到此q.rb,p.rb也不会崩溃。

另一个例子:

  • p.rb
STDOUT.sync = true
loop until print("\e[2K<<<#{Time.now.strftime('%H:%M:%S:%2N')}>>>\r")

[警告:不睡觉的循环可能会占用您的CPU使用率]

  • q.rb
sleep 3

运行:

bash[~] $ time ruby p.rb | q.rb
Traceback (most recent call last):
    2: from p.rb:2:in `<main>'
    1: from p.rb:2:in `print'
p.rb:2:in `write': Broken pipe @ io_write - <STDOUT> (Errno::EPIPE)

real    0m3.186s
user    0m0.282s
sys 0m0.083s

您看到程序在3秒后崩溃了。如果q.rb具有sleep 5,它将在5.1秒后崩溃。同样, q.rb 中的sleep 0将在0.1秒后崩溃 p.rb 。我想额外的0.1秒取决于系统,因为我的系统需要0.1秒来加载ruby解释器。

我编写了p.cr和q.cr Crystal程序进行测试。水晶是compiled,加载不需要很长时间,只需0.1秒。

Crystal程序:

  • p.cr
STDOUT.sync = true
loop do print("\e[2KHi!\r") end rescue exit
  • q.cr
sleep 3

我编译了它们,然后运行:

bash[~] $ time ./p | ./q

real    0m3.013s
user    0m0.007s
sys 0m0.019s

二进制文件 ./ p 在非常接近3秒的时间内处理Unhandled exception: Error writing file: Broken pipe (Errno)并退出。同样,两个Crystal程序可能要花0.01秒才能执行,也许内核还需要一点时间来运行进程。

还要注意,STDERR#printSTDERR#putsSTDERR#putcSTDERR#printfSTDERR#writeSTDERR#syswrite甚至不会引发Errno :: EPIPE如果输出是同步的。

结论

管道为arcane。将STDOUT#sync设置为true或使用STDOUT#flush会将所有缓冲的数据刷新到基础操作系统。

在运行hello.rb | whoami时,如果没有同步,我可以写入8191字节的数据,并且程序 hello.rb 不会崩溃。但是使用同步时,通过管道写入1个字节将导致 hello.rb 崩溃。

因此,当 hello.rb 将标准输出与管道程序whoami同步时,whoami不会等待 hello.rb hello.rb 引发Errno::EPIPE,因为这两个程序之间的管道中断了(如果我在这里迷路了,请纠正我)。