如何通过ssh在shell命令中显示进度条

时间:2009-05-16 12:56:10

标签: ruby command-line ssh

我有一个脚本应该在我的本地机器上模仿ffmpeg,通过将命令发送到远程机器,在那里运行然后返回结果。 (见previous stackoverflow question.

#!/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 个答案:

答案 0 :(得分:7)

在问题的显示方面,您可以使用“\ r”字符串char在Ruby中生成更新进度条。这会将您备份到当前行的开头,允许您重新编写它。例如:

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

或者,如果您只想在屏幕上显示进度条,则可以执行与此类似的操作:

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

此外,与stdout相关,除了前一个示例(STDOUT.flush)的刷新输出之外,您还可以要求Ruby自动将写入与IO缓冲区(在本例中为STDOUT)同步以及相关的设备写入(基本上关闭内部缓冲):

STDOUT.sync = true

另外,我发现有时 flush对我不起作用,而我使用“IO.fsync”代替。对我而言,这主要与文件系统工作有关,但值得了解。

答案 1 :(得分:4)

来自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"]

因此,您可以通过这种方式与正在运行的流程进行交互。

正在运行的进程在请求输入之前刷新其输出是很重要的 - 否则,程序可能会挂起,因为通道可能没有收到未刷新的输出。