RubyonLinuxPty在没有EOF的情况下离开,引发了Errno::EIO

RubyonLinuxPty在没有EOF的情况下离开,引发了Errno::EIO,ruby,debian,ruby-1.9.3,pty,Ruby,Debian,Ruby 1.9.3,Pty,我正在编写一些代码,它获取一个文件,将该文件传递给几个二进制文件中的一个进行处理,并监视转换过程中的错误。我已经在OSX上编写并测试了以下例程,但linux失败的原因我不清楚 #run the command, capture the output so it doesn't display PTY.spawn(command) {|r,w,pid| until r.eof? do ##mark puts r.readline end } 运行的命令变化

我正在编写一些代码,它获取一个文件,将该文件传递给几个二进制文件中的一个进行处理,并监视转换过程中的错误。我已经在OSX上编写并测试了以下例程,但linux失败的原因我不清楚

#run the command, capture the output so it doesn't display
PTY.spawn(command) {|r,w,pid|
    until r.eof? do
      ##mark
      puts r.readline
    end
}
运行的命令变化很大,###标记处的代码已简化为本地回显,以尝试调试该问题。命令执行,脚本在终端中打印预期的输出,然后抛出异常

它在Debian系统上产生的错误是:
Errno::EIO(输入/输出错误-/dev/pts/0):

我能想到的所有命令字符串都会产生该错误,当我在没有本地echo块的情况下运行代码时,它运行得很好:

PTY.spawn(command) {|r,w,pid|}
在这两种情况下,命令本身执行良好,但DebianLinux似乎没有向pty发送eof。在这里,PTY的文档页面和ruby文档上的IO似乎没有任何帮助

有什么建议吗?谢谢


-vox-

在这里提出Errno::EIO似乎是有效的(它只是意味着子进程已经完成并关闭了流),因此您应该期待并捕获它

例如,请参见和中的选定答案


顺便说一句,我做了一些测试。在Ubuntu10.04上的Ruby 1.8.7上,我没有收到任何错误。在Ruby 1.9.3中,我做到了。Ubuntu上的JRuby1.6.4在1.8和1.9两种模式下运行,我没有收到任何错误。在OSX上,使用1.8.7、1.9.2和1.9.3,我没有收到任何错误。这种行为显然取决于您的Ruby版本和平台。

因此我不得不阅读PTY库的C源代码,才能真正满意这里的情况

Ruby PTY文档并没有真正说出评论所说的内容

我的解决方案是组合一个包装器方法,并在需要时从脚本中调用它。我还加入了等待进程的方法,以确保退出以及从
$?
访问退出状态:

# file: lib/safe_pty.rb

require 'pty'
module SafePty
  def self.spawn command, &block

    PTY.spawn(command) do |r,w,p|
      begin
        yield r,w,p
      rescue Errno::EIO
      ensure
        Process.wait p
      end
    end

    $?.exitstatus
  end
end
这与PTY.spawn基本相同:

require 'safe_pty'
exit_status = SafePty.spawn(command) do |r,w,pid|
  until r.eof? do
    logger.debug r.readline
  end
end

#test exit_status for zeroness

我非常沮丧地发现这是一个有效的响应,因为它完全没有记录在ruby doc上。

ruby-doc.org这样说,因为ruby 1.9:

# The result of read operation when pty slave is closed is platform
# dependent.
ret = begin
        m.gets          # FreeBSD returns nil.
      rescue Errno::EIO # GNU/Linux raises EIO.
        nil
      end
好的,现在我知道这种行为在Linux上是“正常的”,但这意味着获取PTY的输出有点棘手。如果您执行
m.read
操作,它会读取所有内容,然后将其丢弃并引发Errno::EIO。您确实需要使用
m.readline
逐块读取内容。即使如此,如果最后一行由于任何原因没有以“\n”结尾,也有可能丢失最后一行。为了更加安全,您需要使用
m.read(1)


关于tty和pty对缓冲的影响的补充说明:它与子进程中的
STDOUT.sync=true
(无缓冲输出)不同,但它会触发行缓冲,其中输出在“\n”

上刷新这只是一个猜测,但readlines是否正确编译为Debian ruby版本?如果这就是问题所在,并且您使用rvm,那么他们关于这个问题的注释可能会有所帮助:这是一个好主意。我没有在这些服务器上使用rvm,但我确实从源代码(以及另一台服务器1.9.2)编译了ruby1.9.3。我没有考虑过更改readline库可能会对其进行排序。感谢您的建议。libreadline与命令行编辑、历史记录等相关(例如在irb中)。它不影响IO#readline方法。您可以在没有libreadline支持的情况下编译Ruby,IO将按预期工作(但irb的使用会令人不快)。很有趣。浏览src时,我在pty_open的评论中发现了这一点:“当pty从机关闭时,读取操作的结果取决于平台”。这实际上也在rdoc中,但我没有看到它。@RichDrummond Yea,这就是我所说的评论。我在rdoc中也没有看到它,但那是因为我在看.spawn的注释,而不是其他任何东西-/赏金奖是因为他是唯一一个知道我在说什么的人!如果在你发布这篇文章之前15分钟我没有在C源代码中发现这个解决方案,我很肯定这会让我得到答案。干杯