当从命令运行时不显示线程输出(从irb运行)
问题描述:
我决定用可执行组件重写一个ruby脚本作为gem /模块。原始的工作正常,但作为一个宝石,更容易维护/安装所有的依赖关系。当从命令运行时不显示线程输出(从irb运行)
该命令接收一组主机和一个命令,并通过线程在主机上运行它。我现在遇到的问题是命令的输出不显示在终端上。然而,从IRB内执行模块代码会产生输出。
模块代码如下:
require "rcmd/version"
require 'net/ssh'
require 'thread'
module Rcmd
@queue = Queue.new
class << self
attr_accessor :nthreads
attr_accessor :user
attr_accessor :quiet
attr_accessor :command
attr_accessor :host_list
attr_accessor :threads
end
# Built in function called by each thread for executing the command on individual hosts
def Rcmd.run_command_on_host(conn_options)
begin
# Create ssh session to host
Net::SSH.start(conn_options[:host], conn_options[:user], :password => conn_options[:passwd]) do |session|
# Open channel for input/output control
session.open_channel do |channel|
channel.on_data do |ch, data|
# Print recieved data if quiet is not true
puts "#{conn_options[:host]} :: #{data}" unless conn_options[:quiet]
end
channel.on_extended_data do |ch,type,data|
# Always print stderr data
puts "#{conn_options[:host]} :: ERROR :: #{data}"
end
# Execute command
channel.exec @command
end
# Loop until command completes
session.loop
end
rescue
puts "#{conn_options[:host]} :: CONNECT ERROR :: Unable to connect to host!\n"
end
end
# Main method of module for starting the execution of the specified command on provided hosts
def Rcmd.run_command()
if not @command
raise ArgumentError.new("No command set for execution")
end
if not @host_list.count >= 1
raise ArgumentError.new("host_list must contain at least one system")
end
@host_list.each do |host|
@queue << host
end
until @queue.empty?
# Don't start more threads then hosts.
num_threads = @nthreads <= @host_list.count ? @nthreads : @host_list.count
# Prepare threads
@threads = { }
num_threads.times do |i|
@threads[i] = Thread.new {
conn_options = { :user => @user, :host => @queue.pop, :password => nil, :quiet => @quiet}
unless conn_options[:host].nil?
self.run_command_on_host(conn_options)
end
}
end
# Execute threads
@threads.each(&:join)
end
end
end
试验片段:
require 'rcmd'
Rcmd.host_list= ["localhost", "dummy-host"]
Rcmd.nthreads= 2
Rcmd.user= 'root'
Rcmd.command= "hostname -f"
Rcmd.run_command
运行上面在IRB生产:
dummy-host :: CONNECT ERROR :: Unable to connect to host!
localhost :: darkstar.lan
正如所预期的。然而,从一个脚本文件(gem命令)或红宝石运行相同的直接结果是无输出:
[email protected]:~/repos/rcmd$ rcmd -n localhost,dummy-host -c 'hostname -f'
[email protected]:~/repos/rcmd$
以前我用$ stdout.puts和$ stderr.puts以及常数变量,但在无奈之下把它移到了放。我也尝试过使用印刷和其他各种方法,包括将流传递给线程,并在线程完成后打印所有输出,但无济于事。
在通过代码添加大量'p'语句之后,当从命令运行时,输出在run_command_on_host方法中的Net :: SSH.start调用之前停止。
我也尝试过在线程创建中使用这个完整的方法,但是却无法完全执行。因此有两种方法。一个用于创建线程,另一个用于执行ssh会话和命令的线程。
在ruby-2.3.3和ruby-2.0.0-p648中都失败了,所以我觉得我只是在做一些愚蠢的事情。
如果有人能够在他们的心中发现它告诉这个Ruby新手他出错了,它将非常感激。
答
我不确定到底是为什么,但似乎方法调用正在退出(不等待线程,尽管线程连接),从而杀死线程。因此,Net :: SSH模块没有时间获取/返回数据。
我添加了下面的除非声明接近代码结束,强制该方法等待线程完成,现在输出显示为所需。还减少了一些代码量。
unless @threads.each.map {|t| t.alive?}.none?
sleep 1
end