我有一个脚本应该在我的本地机器上模仿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)
我只在命令运行完毕后才得到结果输出。我如何更改代码才能使其工作?
答案 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"]
因此,您可以通过这种方式与正在运行的流程进行交互。
正在运行的进程在请求输入之前刷新其输出是很重要的 - 否则,程序可能会挂起,因为通道可能没有收到未刷新的输出。