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:
- Connection is established
- Command is sent
- Output is received
- 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:
- Connection is established.
- Commands are sent as a single, infinite, concatenated stream
- 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
Ruby - How to Retrieve Sum in Array Group by Multiple Keys with Condition Max
How to Verify Pacts Against an API That Requires an Auth Token
Utc Time Resets to 2000-01-01 (Ruby). How to Prevent the Time from Resetting
Don't the Ruby Methods Instance_Eval() and Send() Negate the Benefits of Private Visibility
Rails 4 Order by Virtual Attribute
Count Overlapping Regex Matches in Perl or Ruby
How to Ignore Irrelevant Methods When Profiling Ruby Applications
Instagram Ruby Gem - Unable to Reach Callback Url
Enter & Ioerror: Byte Oriented Read for Character Buffered Io
Connection Refused Using Sunspot and Solr in Rails
Ruby, No Implicit Conversion of Symbol into Integer
How to Set Up These Crud Controller Actions for Has_Many_Polymorphs and an Error
Invalid Route Name, Already in Use: 'Admin_Root' (Argumenterror) - Failed Activeadmin Install
Emulating Int64 Overflows in Ruby
Ruby Before_Validation Triggers Infinite Loop of Call Back