Controlling a Process on Another Machine
Problem
You want to run a process on another machine, controlling its input stream remotely, and reading its output and error streams.
Solution
The ruby-ssh gem, first described in Recipe 14.10, provides a popen3 method that works a lot like Rubys built-in popen3, except that the process you spawn runs on another computer.
Heres a method that runs a Unix command on another computer and yields its standard I/O streams to a code block on your computer. All traffic going between the computers is encrypted with SSL. To authenticate yourself against the foreign host, youll either need to provide a username and password, or set up an SSL key pair ahead of time.
require ubygems require et/ssh def run_remotely(command, host, args) Net::SSH.start(host, args) do |session| session.process.popen3(command) do |stdin, stdout, stderr| yield stdin, stdout, stderr end end end
Here it is in action:
run_remotely(ls -l /home/leonardr/dir, example.com, :username=>leonardr, :password => mypass) { |i, o, e| puts o.read } # -rw-rw-r-- 1 leonardr leonardr 33 Dec 29 20:40 file1 # -rw-rw-r-- 1 leonardr leonardr 102 Dec 29 20:40 file2
Discussion
The Net::SSH library implements a low-level interface to the SSH protocol, but most of the time you don need all that power. You just want to use SSH as a way to spawn and control processes on a remote computer. Thats why Net:SSH also provides a popen3 interface that looks a lot like the popen3 you use to manipulate processes on your own computer.
Apart from the issue of authentication, there are a couple of differences between Net::SSH.popen3 and Open3.popen3. With Open3.popen3, you must be careful to close the standard input stream before reading from the output or error streams. With the Net::SSH version of popen3, you can read from the output or error streams as soon as the process writes any data to it. This lets you interleave stdin writes and stdout reads:
run_remotely(cat, example.com, :username=>leonardr, :password => mypass) do |stdin, stdout, stderr| stdin.puts Line one. puts stdout.read stdin.puts Line two. puts stdout.read end # "Line one." # "Line two."
Another potential pitfall is that the initial working directory for an SSH session is the filesystem root (/). If youve used the ssh or scp commands, you may be accustomed to starting out in your home directory. To compensate for this, you can change to your home directory within your command: issue a command like cd; ls or cd /home/[user name]/; ls instead of just plain ls.
See Also
- The Net::SSH manual at: http://net-ssh.rubyforge.org/
- Recipe 14.2, "Making an HTTPS Web Request," has information on installing the OpenSSL extension that is a prerequisite of ruby-ssh
- Recipe 14.10, "Being an SSH Client covers the basic rules of SSH"
- Recipe 20.8, "Driving an External Process with popen," and Recipe 20.9, "Capturing the Output and Error Streams from a Unix Shell Command," cover the basic features of the popen family of methods
Категории