【问题标题】:How do I display progress bars from a shell command over ssh如何通过 ssh 从 shell 命令显示进度条
【发布时间】:2010-10-05 18:03:45
【问题描述】:

我有一个脚本,它应该在我的本地机器上模仿 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)

我只在命令完成运行后才能得到结果输出。我将如何更改代码才能使其正常工作?

【问题讨论】:

    标签: ruby command-line ssh


    【解决方案1】:

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

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

    或者,如果您只想在屏幕上显示一个进度条,您可以简单地执行类似以下操作:

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

    此外,关于标准输出,除了根据前面的示例 (STDOUT.flush) 刷新输出之外,您还可以要求 Ruby 自动将写入与关联的设备写入同步到 IO 缓冲区(在本例中为 STDOUT)(基本上关闭内部缓冲):

    STDOUT.sync = true
    

    另外,我发现 sometimes 刷新对我不起作用,我改用“IO.fsync”。对我来说,这主要与文件系统工作有关,但值得了解。

    【讨论】:

      【解决方案2】:

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

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

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

      【讨论】:

      • 进度条仍然无法正确显示?对吗?
      猜你喜欢
      • 1970-01-01
      • 2023-03-25
      • 1970-01-01
      • 2013-11-22
      • 1970-01-01
      • 2013-11-30
      • 2013-08-24
      • 2020-09-24
      • 1970-01-01
      相关资源
      最近更新 更多