Как отобразить индикаторы выполнения из команды оболочки через ssh - PullRequest
3 голосов
/ 16 мая 2009

У меня есть скрипт, который должен имитировать ffmpeg на моей локальной машине, посылая команду на удаленную машину, запуская ее там и затем возвращая результаты. (см. предыдущий стек переполнение вопрос. )

#!/usr/bin/env ruby

require 'rubygems'
require 'net/ssh'
require 'net/sftp'
require 'highline/import'


file = ARGV[ ARGV.index( '-i' ) + 1] if ARGV.include?( '-i' )  
puts 'No input file specified' unless file;

host = "10.0.0.10"
user = "user"
prod = "new-#{file}"               # product filename (call it <file>-new)
rpath = "/home/#{user}/.rffmpeg"   # remote computer operating directory
rfile = "#{rpath}/#{file}"         # remote filename
rprod = "#{rpath}/#{prod}"         # remote product
cmd = "ffmpeg -i #{rfile} #{rprod}"# remote command, constructed

pass = ask("Password: ") { |q| q.echo = false }  # password from stdin

Net::SSH.start(host, user ) do |ssh|
        ssh.sftp.connect do |sftp|

                # upload local 'file' to remote 'rfile'
                sftp.upload!(file, rfile)

                # run remote command 'cmd' to produce 'rprod'
                ssh.exec!(cmd)

                # download remote 'rprod' to local 'prod'
                sftp.download!(rprod, prod)
        end
end

теперь моя проблема в

ssh.exec!(cmd)

Я хочу отобразить вывод команды cmd для локального пользователя в в режиме реального времени . Но делая это

puts ssh.exec!(cmd)

Полученный результат я получу только после завершения выполнения команды. Как мне изменить код, чтобы это работало?

Ответы [ 2 ]

7 голосов
/ 20 мая 2009

На стороне вашего вопроса вы можете сгенерировать индикатор прогресса обновления в Ruby, используя строковый символ "\ r". Это возвращает вас к началу текущей строки, позволяя переписать ее. Например:

1.upto(100) { |i| sleep 0.05; print "\rPercent Complete #{i}%"}

Или, если вы просто хотите, чтобы индикатор выполнения отображался на экране, вы можете просто сделать что-то похожее на это:

1.upto(50) { sleep 0.05; print "|"}

Кроме того, что касается stdout, в дополнение к сбросу вывода в предыдущем примере (STDOUT.flush) вы можете попросить Ruby автоматически синхронизировать записи в буфер ввода-вывода (в данном случае STDOUT) с записями, связанными с устройством (в основном отключает внутреннюю буферизация):

STDOUT.sync = true

Кроме того, я обнаружил, что иногда flush не работает для меня, и вместо этого я использую "IO.fsync". Для меня это в основном связано с работой файловой системы, но это стоит знать.

4 голосов
/ 16 мая 2009

С ri Net::SSH::start:

 -------------------------------------------------------- Net::SSH::start
      Net::SSH::start(host, user, options={}, &block) {|connection| ...}
 ------------------------------------------------------------------------
      The standard means of starting a new SSH connection. When used with
      a block, the connection will be closed when the block terminates,
      otherwise the connection will just be returned. The yielded (or
      returned) value will be an instance of
      Net::SSH::Connection::Session (q.v.). (See also
      Net::SSH::Connection::Channel and Net::SSH::Service::Forward.)

        Net::SSH.start("host", "user") do |ssh|
          ssh.exec! "cp /some/file /another/location"
          hostname = ssh.exec!("hostname")

          ssh.open_channel do |ch|
            ch.exec "sudo -p 'sudo password: ' ls" do |ch, success|
              abort "could not execute sudo ls" unless success

              ch.on_data do |ch, data|
                print data
                if data =~ /sudo password: /
                  ch.send_data("password\n")
                end
              end
            end
          end

          ssh.loop
        end

Похоже, вы можете получить больше интерактивности, используя #open_channel

Вот пример кода:

user@server% cat echo.rb
#! /usr/local/bin/ruby
def putsf s
  puts s
  STDOUT.flush
end
putsf "hello"
5.times do
        putsf gets.chomp
end
putsf "goodbye"

А на вашей локальной машине:

user@local% cat client.rb
#! /usr/local/bin/ruby
require 'rubygems'
require 'net/ssh'
words = %w{ earn more sessions by sleaving }
index = 0;
Net::SSH.start('server', 'user') do |ssh|
  ssh.open_channel do |ch|
    ch.exec './echo.rb' do |ch, success|
      abort "could not execute ./echo.rb" unless success

      ch.on_data do |ch, data|
        p [:data, data]
        index %= words.size
        ch.send_data( words[index] + "\n" )
        index += 1
      end
    end
  end
end
user@local% ./client.rb
[:data, "hello\n"]
[:data, "earn\n"]
[:data, "more\n"]
[:data, "sessions\n"]
[:data, "by\n"]
[:data, "sleaving\n"]
[:data, "goodbye\n"]

Таким образом, вы можете взаимодействовать с запущенным процессом таким образом.

Важно, чтобы выполняющийся процесс сбрасывал свой вывод перед запросом ввода - в противном случае программа может зависнуть, так как канал может не получить незаполненный вывод.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...