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
我真的很欣赏你所能提供的任何见解。
- 难道你就不能把stdout输出输入你的脚本中的用户吗?
- (捕捉+分析后)
- 感谢您的回复!如果我能一个字符一个字符地读取命令的stdout以在终端中打印它(就像手动tee一样),这就可以了,但是我认为我不能这样做,因为输出将被缓冲。这在没有
的线路(如jmate@ssh_server~$ )时尤其有问题。
- 我认为通过popen3,你可以得到一个IO对象,你可以在没有缓冲或超时的情况下读取它。此外,您还可以执行类似于stdin.copy_stream($stdin)的操作。
我找到了答案!非常感谢@kimmo lehto提出了IO.copy_stream()的想法,这导致了我的最终解决方案。基本上,我们使用PTY.spawn(cmd){|stdin, stdout, pid| ...}获得stdin和stdout的无缓冲流。
然后,我们将脚本的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)。