如何使用Ruby的Net :: SSH库获得退出状态?

Lee*_*ley 31 ruby ssh net-ssh

我有一段代码,只是试图在远程服务器上执行一个脚本,如果它失败了,我想打个电话跟进,想象一下:

require 'rubygems'
require 'net/ssh'
require 'etc'

server = 'localhost'

Net::SSH.start(server, Etc.getlogin) do |ssh|
  puts (ssh.exec("true")  ? 'Exit Success' : "Exit Failure")
  puts (ssh.exec("false") ? 'Exit Success' : "Exit Failure")  
end
Run Code Online (Sandbox Code Playgroud)

我希望(忽略stdout和stderr打印在我的设计示例中) - 但是第一行应该退出0,我希望Ruby会与之交互false并显示"退出失败"(当然,逻辑是错误的,三元需要被翻转) - 但第二行应以相反的状态退出,但事实并非如此.

我甚至无法在文档中找到有关如何执行此操作的任何内容,我有点担心我可能做错了吗?!

fli*_*ald 72

我发现使用Net :: SSH运行进程的以下方式更有用.它为您提供了独特的stdoutstderr,exit codeexit 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
Run Code Online (Sandbox Code Playgroud)

希望这可以帮助.

  • 注意... exit-signal返回一个字符串,因此它应该是exit_signal = data.read_string (8认同)
  • 我发现这在我的脚本中非常有用,所以这里有一个包装这个函数的宝石:http://rubygems.org/gems/ssh-exec,源代码为https://github.com/mbautin/ssh-exec. (2认同)

Mik*_*key 7

基于flitzwald的答案 - 我已经将我的版本修改为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
Run Code Online (Sandbox Code Playgroud)