I wrote Composite Primary Keys and Dr Nic’s Magic Models because we have a 50+ table legacy database and I am terrorised by explicit SQL statements on a daily basis. I have a dirty secret – I don’t use either of them at work. We’ve been unsuccessful installing Ruby on our HP-UX box, then time passed, life moved on for the people involved, and so no Ruby. Ergo, SQL hell for Nicholas.
Until today. No, we still don’t have Ruby installed on our HP box, but I do have it installed everywhere else in my life. So I guess I can still access the database remotely… that’s what the TCP/IP internet protocol is for… and for executing shell commands why not remote shell into the box?
Let’s skip over why I never thought of either of these until now. Its bad for my ego. The upside of this minor revelation is that the Composite Primary Keys project will have Oracle support soon.
The point of this article though is to discuss the wonders of using remote ssh (aka remote shell) to invoke shell commands via ruby without having ruby installed locally.
I made it work, it rocks and I shall briefly sing the praises for Jamis Buck. Jamis, you rock.
net-ssh is a lovely API for performing remote shell commands all from ruby. Here’s a demo of what I can do (excluding the setup code which is discussed later):
>> print shell.pwd.stdout /home/senwilli >> shell.cd 'demo' => #<struct Net::SSH::Service::Shell::SyncShell::CommandOutput stdout="", stderr=nil, status=0> >> out = shell.ls '-lart'; print out.stdout total 16 drwxr-xr-x 2 senwilli sv 96 Sep 22 11:56 . drwxr-xr-x 5 senwilli sv 8192 Sep 22 11:56 .. >> out = shell.send_command 'ls -lart'; print out.stdout total 16 drwxr-xr-x 2 senwilli sv 96 Sep 22 11:56 . drwxr-xr-x 5 senwilli sv 8192 Sep 22 11:56 ..
I’ve deliberately been inconsistent in the way I called the shell commands and how I handled the responses to show you some interesting things.
shell is our shell object (which we’ll cover in a bit) and it has delightfully overridden
method_missing so I can send it any old shell command I like. In the example above, I call the commands:
ls using this method. The first parameter is the arguments you’d like to pass to the command. An optional second parameter is for any
stdin you’d like to pass to the process during its execution.
The last command is a duplicate of the previous command, showing that you can explicitly pass a complete shell expression string via the
The result of all shell commands is a struct containing
stderr strings. So, to return the response of the command to the user or logger, you’ll need to store the response object (as per the
out variable above) and use
stdout? to test if
stdout has anything in it.
In the example above, all these commands are executedly being executed synchronously, so I’m getting the stdout/stderr results back before the next command is executed. This is due to the way I created the Shell object. So, let’s look at that.
Creating your Shell object
First you’ll need to install the following gems (from rubyforge.org) in this order:
In your ruby script/console:
require 'net/ssh' session = Net::SSH.start( 'host', 'user', 'passwd' )
To create our synchronous shell:
shell = session.shell.sync
And you’re off and racing.
Jamis has provided lots of useful syntatic sugar (for example, you can pass Net::SSH.start a block with session as its argument, and reading all the docco is a clever thing to do next.
The only thing I couldn’t immediately figure out is how to invoke
sudo su - envname and then continue to control my shell as per normal. All commands just blocked.
Luckily there is a mailing list for me to loiter in…
- Validate and Save your Ruby in TextMate – with secret Rubinus superpowers In some TextMate bundles, if you save a file it...
- Future proofing your Ruby code. Ruby 1.9.1 is coming. Bugger. I’m a Ruby monogamist. I use the Ruby...
- Unit Testing iPhone apps with Ruby: rbiphonetest Everything to love about Ruby: the concise, powerful language;...
- Using Ruby within TextMate snippets and commands I didn’t know you could run Ruby within TextMate snippets....
- The explicit Ruby metaclass you know you always wanted When you define a “static” or “class” method on a...