如何从ruby执行远程主机上的交互式shell程序

dce*_*ere 1 ruby ssh bash shell

我试图从另一个ruby程序在远程主机上执行交互式shell程序.为简单起见,我们假设我想要执行的程序是这样的:

puts "Give me a number:"
number = gets.chomp()
puts "You gave me #{number}"
Run Code Online (Sandbox Code Playgroud)

到目前为止最成功的方法是使用我从这里获得的方法.就是这个:

require 'open3'
Open3.popen3("ssh -tt root@remote 'ruby numbers.rb'") do |stdin, stdout, stderr|
    # stdin  = input stream
    # stdout = output stream
    # stderr = stderr stream
    threads = []
    threads << Thread.new(stderr) do |terr|
        while (line = terr.gets)
          puts "stderr: #{line}"
        end
    end
    threads << Thread.new(stdout) do |terr|
        while (line = terr.gets)
          puts "stdout: #{line}"
        end
    end

    sleep(2)
    puts "Give me an answer: "
    answer = gets.chomp()
    stdin.puts answer


    threads.each{|t| t.join()} #in order to cleanup when you're done.
end
Run Code Online (Sandbox Code Playgroud)

问题是这对我来说不够"互动",我想要执行的程序(不是简单的numbers.rb)有更多的输入/输出.你可以把它想象成一个apt-get install会要求你提供一些输入来解决一些问题.
我读过net :: ssh和pty,但看不出它们是否是我想要的(简单/优雅)解决方案.

理想的解决方案是使用户不会意识到IO正在远程主机上完成:stdin转到远程主机stdin,来自远程主机的stdout来到我身边然后显示它.

如果您有任何想法我可以尝试,我会很高兴听到他们.谢谢!

dgo*_*o.a 6

试试这个:

require "readline"
require 'open3'

Open3.popen3("ssh -tt root@remote 'ruby numbers.rb'") do |i, o, e, th|


  Thread.new {
    while !i.closed? do
      input =Readline.readline("", true).strip 
      i.puts input
    end
  }

  t_err = Thread.new {
    while !e.eof?  do
      putc e.readchar
    end
  }

  t_out = Thread.new {
    while !o.eof?  do
      putc o.readchar
    end
  }

  Process::waitpid(th.pid) rescue nil 
  # "rescue nil" is there in case process already ended.

  t_err.join
  t_out.join

end
Run Code Online (Sandbox Code Playgroud)

我得到了它的工作,但不要问我为什么它的工作原理.主要是试验/错误.

备择方案:

  • 使用Net :: SSH,你需要使用:on_process和一个Thread:ruby net/ssh通道死掉?别忘了添加session.loop(0.1).链接中的更多信息.Thread /:on_process的想法激发了我为自己编写一个宝石:https://github.com/da99/Chee/blob/master/lib/Chee.rb
  • 如果您的Ruby程序中的最后一次调用是SSH,那么您可以exec ssh -tt root@remote 'ruby numbers.rb'.但是,如果您仍然希望在它们之间进行交互User<->Ruby<->SSH,那么之前的替代方案是最好的.