How to get exit status with Ruby's Net::SSH library?

后端 未结 3 2060
囚心锁ツ
囚心锁ツ 2021-01-30 02:32

I have a snippet of code, simply trying to execute a script on a remote server, in the event that it fails, I\'d like to make a follow-up call, imagine this:

req         


        
相关标签:
3条回答
  • 2021-01-30 03:12

    Building on the answer by flitzwald - I've monkey patched my version of this into Net::SSH (Ruby 1.9+)

    class Net::SSH::Connection::Session
      class CommandFailed < StandardError
      end
    
      class CommandExecutionFailed < StandardError
      end
    
      def exec_sc!(command)
        stdout_data,stderr_data = "",""
        exit_code,exit_signal = nil,nil
        self.open_channel do |channel|
          channel.exec(command) do |_, success|
            raise CommandExecutionFailed, "Command \"#{command}\" was unable to execute" unless success
    
            channel.on_data do |_,data|
              stdout_data += data
            end
    
            channel.on_extended_data do |_,_,data|
              stderr_data += data
            end
    
            channel.on_request("exit-status") do |_,data|
              exit_code = data.read_long
            end
    
            channel.on_request("exit-signal") do |_, data|
              exit_signal = data.read_long
            end
          end
        end
        self.loop
    
        raise CommandFailed, "Command \"#{command}\" returned exit code #{exit_code}" unless exit_code == 0
    
        {
          stdout:stdout_data,
          stderr:stderr_data,
          exit_code:exit_code,
          exit_signal:exit_signal
        }
      end
    end
    
    0 讨论(0)
  • 2021-01-30 03:15

    For newer versions of Net::SSH, you can just pass a status hash to Net::SSH::Connection::Session#exec:

    status = {}
    
    Net::SSH.start(hostname, user, options) do |ssh|
      channel = ssh.exec(command, status: status)
      channel.wait # wait for the command to actually be executed
    end
    
    puts status.inspect
    # {:exit_code=>0}
    

    By default, exec streams its output to $stdout and $stderr. You can pass a block to exec to do something different, a la:

    ssh.exec(command, status: status) do |ch, stream, data|
      if stream == :stdout
        do_something_with_stdout(data)
      else
        do_something_with_stderr(data)
      end
    end
    

    This works on 6.1.0 - not sure about availability for older versions. See http://net-ssh.github.io/net-ssh/Net/SSH/Connection/Session.html#method-i-exec for more details.

    0 讨论(0)
  • 2021-01-30 03:21

    I find the following way of running processes with Net::SSH much more useful. It provides you with distinct stdout and stderr, exit code and exit signal.

    require 'rubygems'
    require 'net/ssh'
    require 'etc'
    
    server = 'localhost'
    
    def ssh_exec!(ssh, command)
      stdout_data = ""
      stderr_data = ""
      exit_code = nil
      exit_signal = nil
      ssh.open_channel do |channel|
        channel.exec(command) do |ch, success|
          unless success
            abort "FAILED: couldn't execute command (ssh.channel.exec)"
          end
          channel.on_data do |ch,data|
            stdout_data+=data
          end
    
          channel.on_extended_data do |ch,type,data|
            stderr_data+=data
          end
    
          channel.on_request("exit-status") do |ch,data|
            exit_code = data.read_long
          end
    
          channel.on_request("exit-signal") do |ch, data|
            exit_signal = data.read_long
          end
        end
      end
      ssh.loop
      [stdout_data, stderr_data, exit_code, exit_signal]
    end
    
    Net::SSH.start(server, Etc.getlogin) do |ssh|
      puts ssh_exec!(ssh, "true").inspect
      # => ["", "", 0, nil]
    
      puts ssh_exec!(ssh, "false").inspect  
      # => ["", "", 1, nil]
    
    end
    

    Hope this helps.

    0 讨论(0)
提交回复
热议问题