2017-06-14 174 views
0

我決定用可執行組件重寫一個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新手他出錯了,它將非常感激。

回答

0

我不確定到底是爲什麼,但似乎方法調用正在退出(不等待線程,儘管線程連接),從而殺死線程。因此,Net :: SSH模塊沒有時間獲取/返回數據。

我添加了下面的除非聲明接近代碼結束,強制該方法等待線程完成,現在輸出顯示爲所需。還減少了一些代碼量。

unless @threads.each.map {|t| t.alive?}.none? 
    sleep 1 
end