Thursday, September 25, 2008

Run shell command in Ruby

Ruby supports the typical ways to run shell command - exec, system and backticks (``). You can google search and get info on them. Its pretty easy. This post is for those who tried these methods and still want more than what they provide. The issue with those methods are they do not give us stdout, exit-status and stderr easily.

So I recommend using open4 instead.

Here is how you do it :-

1. gem install open4
2. cd /usr/lib/ruby/gems/1.8/gems/open4-0.9.6/ (or whatever version you got)
3. ruby install.rb (to install open4. This copies open4.rb file into your ruby lib)
4. Write your code similar to the one below :-

require "open4"
pid, stdin, stdout, stderr = Open4::popen4 "sh"
stdin.puts "mount -t cif //172.20.30.18/write_shared /mnt/cifs -o user=name1,password=password1"
stdin.close
ignored, status = Process::waitpid2 pid
puts "pid : #{ pid }"
puts "stdout : #{ stdout.read.strip }"
puts "stderr : #{ stderr.read.strip }"
puts "status : #{ status.inspect }"
puts "exitstatus : #{ status.exitstatus }"

output:-

pid : 6441
stdout :
stderr : mount: unknown filesystem type 'cif'
status : #
exitstatus : 32


This is what this code does. It opens a shell and gives the mount command to its stdin. So its like you manually opening a shell and typing in the command there. The pid, output, error and status is now available in pid, stdin, stdout and stderr.

In my code I deliberately made a mistake of mount type cif instead of cifs. So It gives me the error in stderr and the exit code 32.

To do the same trick on Windows, use popen4. Here is the link to it. Click on the Class and you will get a sample code also.


After a lot of days, here I found a better way of doing the same thing. Instead of using "open4", use "systemu". I had to look for alternatives, because one day all of a sudden my code written using open4 started hanging. So I searched and found "systemu". This is more elegant and easier to do.

Install systemu using gem install and goto its directory in /usr/lib/ruby/gems and then run "ruby install.rb". Once the installation is over, use it as follows :-

require 'systemu'
cmd="date"
status, stdout, stderr = systemu cmd

No comments: