14

我正在编写一些代码,它接收一个文件,将该文件传递给几个二进制文件之一进行处理,并监视转换过程中的错误。我已经在 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
}

运行的命令变化很大,##mark 处的代码已被简化为本地回显,以尝试调试问题。该命令执行,脚本在终端中打印预期的输出,然后引发异常。

它在 Debian 系统上产生的错误是: Errno::EIO (Input/output error - /dev/pts/0):

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

PTY.spawn(command) {|r,w,pid|}

在任何一种情况下,命令本身都可以正常执行,但似乎 debian linux 没有将 eof 发送到 pty。PTY 的文档页面和 ruby​​-doc 上的 IO 似乎在这里没有任何帮助。

有什么建议么?谢谢。

-vox-

4

3 回答 3

19

所以我不得不去阅读 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 上完全没有记录。

于 2012-04-24T22:12:17.783 回答
5

在这里提出 Errno::EIO 似乎是有效的(它只是意味着子进程已经完成并关闭了流),所以你应该期待并抓住它。

例如,请参阅Continuously read from STDOUT of external process in Rubyhttp://www.shanison.com/2010/09/11/ptychildexited-exception-and-ptys-exit-status/中选择的答案

顺便说一句,我做了一些测试。在 Ubuntu 10.04 上的 Ruby 1.8.7 上,我没有收到错误消息。使用 Ruby 1.9.3,我愿意。在 Ubuntu 上使用 1.8 和 1.9 模式下的 JRuby 1.6.4,我没有收到错误。在 OS X 上,使用 1.8.7、1.9.2 和 1.9.3,我没有收到错误消息。该行为显然取决于您的 Ruby 版本和平台。

于 2012-04-24T21:45:48.623 回答
0

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与子进程中的 (unbuffered output) 不同,而是触发line buffering,其中输出在 "\n" 上刷新

于 2019-04-18T15:11:25.667 回答