2017-06-14 5 views
0

実行可能なコンポーネントを持つ宝石/モジュールとしてRubyスクリプトを書き直すことにしました。オリジナルはうまく動作しますが、宝石としてすべての依存関係を維持/インストールする方が簡単です。コマンドから実行したときにスレッド出力が表示されない(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コマンド)から、またはRubyで同じことを実行すると、直接出力なしで結果:

[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コールの直前で停止します。

また、スレッド作成内でそのメソッド全体を実行しようとしましたが、完全に実行できませんでした。したがって、2つの方法がある。スレッドを作成するためのものと、sshのセッションとコマンドを実行するためにスレッドによって使用されるもの。

ruby​​-2.3.3とruby-2.0.0-p648の両方で失敗しているので、私は何かばかげていると思う。

誰かが自分の心の中でこのRuby初心者に何か間違っていると言ってもらうと、非常に感謝しています。

答えて

0

私は正確にはわからないが、メソッド呼び出しが終了している(スレッド結合にもかかわらずスレッドを待たずに)スレッドを強制終了しているようだ。したがって、Net :: SSHモジュールにはデータを取得/返す時間がありませんでした。

コードの末尾付近に次のunlessステートメントを追加して、メソッドがスレッドの完了を待つようにし、出力が必要に応じて表示されるようにしました。また、コードの量を少し減らしました。

unless @threads.each.map {|t| t.alive?}.none? 
    sleep 1 
end 
関連する問題