How to Get Exit Status with Ruby's Net::Ssh Library

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

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.

Ruby Net::SSH get Exit Status in PTY

You may want to take a look at the source for the Ruby "ssh" gem and "remote_task" gem.

  • http://rubygems.org/gems/ssh

The relevant source code is here:

  • https://github.com/seattlerb/ssh/blob/master/lib/ssh.rb

It has code for handling the "cd" command, i/o channels, and the exit status.

Net::SSH `wget` progress bar

In this case it looks like wget is the culprit and not the carriage returns I suspected.

There are two progress output modes bar and dot. Bar is default if the output is a tty (which is what I have locally) but dot is the default in non-tty sessions (which is what I have via ssh). The dot progress bar does not have carriage returns. So that explains why I'm not seeing them.

Adding the option --progress=bar:force to the wget call works as expected, carriage returns and all.

I'll keep this here and rename the question a bit in case anyone every stumbles into this issue.

Ruby Net-SSH-Shell get standard out from execute! method

Have you tried doing it this way?

Net::SSH.start('host','user') do |ssh|
ssh.shell do |bash|
process = bash.execute! 'ls'
process.on_output do |a, b|
# a is the process itself, b is the output
p [a, b]
end
end
end

You can have a look at the definition of Net::SSH::Process here: https://github.com/mitchellh/net-ssh-shell/blob/master/lib/net/ssh/shell/process.rb

EDIT

I think the problem lies with the ! in execute! because the following works fine for me:

require 'net/ssh'
require 'net/ssh/shell'

Net::SSH.start('students.iitmandi.ac.in', 'k_vikhyat') do |ssh|
ssh.shell do |bash|
process = bash.execute 'whoami'
process.on_output do |a, b|
p b
end
bash.wait!
bash.execute! 'exit'
end
end

I am not sure why this is the case, because it looks like execute! creates a process, runs wait! and then returns the process.

How do I get the exit status for a command invoked with IO.popen?

You can capture the exit status of a command invoked via IO.open() by referencing $? as long as you have closed the pipe at the end of your block.

In the example above, you would do:

  process = IO.popen("sudo -u service_user -i start_service.sh") do |io|
while line = io.gets
line.chomp!
process_log_line(line)
end
io.close
do_more_stuff if $?.to_i == 0
end

See Ruby Core Library entry for IO.popen for more information.

Net::SSH::Shell::Process $DONTEVERUSETHIS

TL;DR: It's the machine readable equivalent of a colored shell prompt. It's there to tell the library when the issued command has finished, and whether it was successful.


When running a command with Net::SSH (not ::Shell), here's what happens:

  1. Connection is established
  2. Command is sent
  3. Output is received
  4. The command exits, sshd returns the exit code and ends the connection.

This means that it's easy to:

  • Get the output: just read until sshd closes the connection.
  • Get the exit code. sshd returns it.

However, it means that each command is run in a separate session, so cd /tmp followed by pwd will return /home/youruser because these are two different sessions, so the former doesn't affect the latter.


The purpose of Net::SSH::Shell is instead to run multiple, individual commands in the same shell session:

  1. Connection is established.
  2. Commands are sent as a single, infinite, concatenated stream
  3. Output is received as a single, infinite, concatenated stream

This leaves two open questions:

  • How do you know whether the command has finished or whether it's still processing?
  • How do you get the exit code now that sshd doesn't return it?

The way Net::SSH::Shell solves this is by modifying the command in the way you saw, to make it print a unique ID and exit code when done:

  • To get the command's output, read until a line with the unique ID is printed.
  • To get the exit code, read it from the same line.

How to properly close a Net::SSH connection?

If you start an SSH connection without a block, you'll get a Net::SSH::Connection::Session, on which you should eventually call close.

Here is a Net::SSH demonstration program, plus some images and links to interactive visualizations of how Net::SSH works overall, including close.



Related Topics



Leave a reply



Submit