Ruby Net-SSH-Shell get standard out from execute! method
Question
How do I get stdout from the .execute
method in Net-SSH-Shell
With good 'ol Net-SSH, this is easy
Net::SSH.start('host','user') do |ssh|
puts ssh.exec! 'date'
end
Gives me Tue Jun 19 23:43:53 EDT 2012
but if I try to use a shell, I get a process object
Net::SSH.start('host','user') do |ssh|
ssh.shell do |bash|
output = bash.execute! 'ls'
puts output
end
end
Gives me #<Net::SSH::Shell::Process:0x007fc809b541d0>
I can't find anything in the sparse docs about how to get standard out easily. There is the on_output
method, but that doesn't seem to do anything for me if I use the execute!
method
I've also tried passing a block to .execute!
like
bash.execute! 'ls' do |output|
puts output
end
but I still get a process #<Net::SSH::Shell::Process:0x007fc809b541d0>
I need the standard out in a variable that I can use and I need an actual stateful login shell, which barebones Net-SSH doesn't do to my knowledge.
Any ideas?
Edit
Along the same ideas of @vikhyat's suggestion, I tried
ssh.shell do |bash|
process = bash.execute! 'ls' do |a,b|
# a is the process itself, b is the output
puts [a,b]
output = b
end
end
But b
is always empty, even when I know the command returns results.
Solution
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.
OTHER TIPS
I'm using next wrapper
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
And usage:
Net::SSH.start(@ssh_host, @ssh_user, :password => @ssh_password) do |ssh|
result = ssh_exec! ssh, command
end
Found it sometime at SO or somewhere else, don't remember now.