我正在尝试从另一个ruby程序在远程主机上执行交互式shell程序。为了简单起见,我们假设我想要执行的程序是这样的:
puts "Give me a number:"
number = gets.chomp()
puts "You gave me #{number}"
到目前为止最成功的方法是使用我从here获得的方法。就是这个:
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
问题是这对我来说不够“交互”,而我想要执行的程序(不是简单的numbers.rb)有更多的输入/输出。您可以将其视为apt-get install
,它会要求您提供一些输入以解决某些问题
我读过net :: ssh和pty,但看不出它们是否是我想要的(简单/优雅)解决方案。
理想的解决方案是使用户不会意识到IO正在远程主机上完成:stdin转到远程主机stdin,来自远程主机的stdout来找我我展示了它。
如果您有任何想法我可以尝试,我会很高兴听到他们。谢谢!
答案 0 :(得分: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
我开始工作,但不要问我为什么会这样。这主要是试错。
备选方案:
session.loop(0.1)
。链接中的更多信息。 Thread /:on_process的想法激励我为自己编写一个gem:https://github.com/da99/Chee/blob/master/lib/Chee.rb exec ssh -tt root@remote 'ruby numbers.rb'
。但是,如果你仍然想要User<->Ruby<->SSH
之间的互动,那么之前的选择是最好的。