To make interacting with shells, simpler, version 0.9 of Net::SSH introduced the _shell service_. This allows you to open a shell (with or without a pty), send a series of commands to the shell, and then wait for the output from the shell.

{{{lang=ruby,number=true,caption=Using the shell service
Net::SSH.start( 'localhost' ) do |session|

  shell = session.shell.open

  # script what we want to do
  shell.pwd
  shell.cd "/"
  shell.pwd
  shell.test "-e foo"
  shell.cd "/really/bogus/directory"
  shell.send_data "/sbin/ifconfig\n"
  shell.pwd
  shell.ruby "-v"
  shell.cd "/usr/lib"
  shell.pwd
  shell.exit

  # give the above commands sufficient time to terminate
  sleep 0.5

  # display the output
  $stdout.print shell.stdout while shell.stdout?
  $stderr.puts "-- stderr: --"
  $stderr.print shell.stderr while shell.stderr?

end
}}}

Any unrecognized method that is sent to the shell object is interpreted as a command to send to the server. To explicitly send a command, use the @#send_data@ method (but remember to add a newline!). The @#send_data@ method may also be used to send data to any process running in the shell as that process' @stdin@ stream.

You can also specify that a pty should be allocated for this shell:

{{{lang=ruby,number=true,caption=Allocating a pty for the shell
Net::SSH.start( 'localhost' ) do |session|

  shell = session.shell.open( :pty => true )

  # or

  shell = session.shell.open( :pty => { ... } )

end
}}}

If you give a hash for the @:pty@ option, it must be a map of the options that describe the new pty. See the API documentation for the @Channel#request_pty@ method for more information.

Note that this is still an asynchronous approach. You send all the commands through the pipe and then wait for the output. (And be sure to give sufficient time or the processes to terminate!) This is fine for scripts that you just want to throw at the server, but many times you want a more interactive interface, for executing a command and receiving its output before moving on.