关于linux:捕获从Ruby内部运行的交互式外部命令的输出

Capture output of interactive external command run from inside Ruby

我一直在寻找一种运行交互式命令(如ssh)并捕获其输出的方法。这听起来很简单,但据我所知,我的用例和许多谷歌和S/O结果略有不同。

很明显,我的意思是,从终端运行my_ruby_script.rb的用户应该能够

  • 输入(stdin运行的交互系统命令(例如,通过ssh隧道的ls类型)
  • 从(stdout/stderr中读出正在运行的交互系统命令(又如在ssh隧道中看到ls的输出)。
  • 除此之外,我还想读取系统命令(ssh的输出,因此,例如,我可以从我的ruby脚本中检查错误。我得到的最接近的是简单的tee,输出到另一个文件。

    1
    system('ssh username@ssh_server |& tee ssh_log.txt')

    这不是理想的

  • 它需要保存到单独的文件/从单独的文件读取
  • 它需要更复杂的系统调用,这可能是可移植的,也可能不是可移植的
  • 据我了解,大多数在线解决方案(backticks、Open3.*PTY.spawn()等)将stdin/stdout从终端接口重定向到ruby程序。我要找的是让stdin单独使用,并让stdout使用我的ruby脚本。有人有什么想法吗?

    我需要注意的是,system('ssh username@ssh_server')完全按照我想要的做,我只需要能够读取输出。此外,我不希望使用net ssh或任何其他非stdlib库。

    研究:

  • 这可能是描述在Ruby中运行命令的最全面的页面(何时使用Ruby中启动子进程的每个方法以及链接的博客文章)。
  • 这些人(在我看来)有着完全相同的问题:
    • ruby io.popen或open3多写/读操作
    • https://www.ruby-forum.com/t/open3-and-ssh/147475
  • 这些人通过Ruby使用ssh,但他们的脚本不是"交互式的"
    • 从Ruby运行交互式程序
    • https://content.pivotal.io/blog/using-ruby-expect-library-to-reboot-ruckus-wireless-access-points-via-ssh
  • 我真的很欣赏你所能提供的任何见解。


    我找到了答案!非常感谢@kimmo lehto提出了IO.copy_stream()的想法,这导致了我的最终解决方案。基本上,我们使用PTY.spawn(cmd){|stdin, stdout, pid| ...}获得stdinstdout的无缓冲流。

    然后,我们将脚本的stdin复制到命令的stdin,使用IO.copy_stream($stdin.raw!, $stdin)(不要忘记在后面设置$stdin.cooked!)。

    我不知道如何在命令的stdout上使用copy_stream两次(第一次是脚本的stdout,第二次是缓冲区,例如io = StringIO.new),所以我不得不手动读取每个字符,就像这样:stdout.readchar(|c| print c; io.write c)