Как выполнить интерактивную оболочку на удаленном хосте из ruby - PullRequest
1 голос
/ 08 марта 2012

Я пытаюсь запустить интерактивную программу оболочки на удаленном хосте из другой программы ruby. Для простоты предположим, что программа, которую я хочу выполнить, выглядит примерно так:

puts "Give me a number:"
number = gets.chomp()
puts "You gave me #{number}"

Подход, который наиболее успешен до сих пор, заключается в использовании того, что я получил от здесь . Вот этот:

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, но не мог понять, будут ли они (легким / элегантным) решением, которое я ищу.

Идеальным решением будет сделать так, чтобы пользователь не осознавал, что ввод-вывод выполняется на удаленном хосте: стандартный ввод идет на удаленный хост, стандартный вывод, стандартный вывод с удаленного хоста приходит ко мне. и я показываю это.

Если у вас есть идеи, которые я могу попробовать, я буду рад их услышать. Спасибо!

1 Ответ

6 голосов
/ 24 апреля 2012

Попробуйте это:

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

У меня это работает, но не спрашивайте меня, почему это работает. В основном это был метод проб / ошибок.

Альтернативы:

  • Используя Net :: SSH, вам нужно использовать: on_process и поток: канал 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, тогда предыдущий вариант является лучшим.
...