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

时间:2018-09-26 07:45:39

标签: ruby linux bash ssh stdout

我一直在寻找一种运行交互式命令(例如ssh)并捕获其输出的方法。听起来很简单,但据我了解,我的用例与许多google和s / o结果略有不同。

请注意,通过交互,我的意思是,从终端运行my_ruby_script.rb的用户应该能够

  1. 键入({stdin)运行中的交互式系统命令(例如,通过ls隧道键入ssh
  2. 从(stdout / stderr)中读取正在运行的交互式系统命令(例如,再次在ls隧道中看到ssh的输出)

最重要的是,我想阅读系统命令(ssh)的输出,以便例如从Ruby脚本中检查错误。我得到的最接近的结果只是将输出tee简单地复制到另一个文件。

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

这不理想

  1. 它需要保存到一个单独的文件中/从另一个文件中读取
  2. 它需要更复杂的系统调用,它可能是可移植的,也可能是不可移植的

据我了解,大多数在线解决方案(反引号,Open3.*PTY.spawn()等)将stdin / stdout从终端界面重定向到ruby程序。我要寻找的是让stdin独自一人,然后将stdout放入我的Ruby脚本中。有人有什么主意吗?

我应该注意system('ssh username@ssh_server')确实可以满足我的要求,我只需要能够读取输出即可。此外,我不想使用net-ssh或任何其他非stdlib库。

研究:

  1. 这可能是最全面的页面,描述Ruby中的运行命令(When to use each method of launching a subprocess in Ruby和链接的博客文章)。
  2. 这些人(在我看来是)有我完全相同的问题:
  3. 这些人通过Ruby使用ssh,但是他们的脚本不是“交互式的”

我非常感谢您可以提供的任何见解。

1 个答案:

答案 0 :(得分:1)

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

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

我无法弄清楚如何在命令的copy_stream上两次使用stdout(首先是脚本的stdout,然后是缓冲区,例如io = StringIO.new),所以我必须手动读取每个字符,例如:stdout.readchar(|c| print c; io.write c)