In ruby, there are a few ways for you to run a command, however, so far, I am only able to find a way to capture the output from running the command in realtime, meaning that for every output that running command/programming print out, i need to be able to capture it. This article explains different ways of running a command in ruby and how do you capture the output in realtime if you want to do so.

1. Use system kernel method

system("ruby test.rb")

Use it with syntax system(cmd [, arg, …]). Executes cmd in a subshell, returning true if the command was found and ran successfully, false otherwise. An error status is available in $?.

Do note that this method would block the script until the command has finished running. If you wanna make the process a background process, then you can add the ampersand at the end of the command or add ‘start’ at the beginning of the command if it is under windows environment.

system("ruby test.rb &")

2. Use ruby IO class’s popen method

IO.popen ("date") { |f| puts f.gets }

The above will print out the current date. Under ruby’s IO class, this popen is under Ruby’s IO class. The following description are taken from Rdoc. If you need more details, you can go to Rdoc’s IO class.

It runs the specified command string as a subprocess; the subprocess‘s standard input and output will be connected to the returned IO object. If cmd_string starts with a “-’’, then a new instance of Ruby is started as the subprocess. The default mode for the new file object is “r’’, but mode may be set to any of the modes listed in the description for class IO.

If a block is given, Ruby will run the command as a child connected to Ruby with a pipe. Ruby‘s end of the pipe will be passed as a parameter to the block. At the end of block, Ruby close the pipe and sets $?. In this case IO::popen returns the value of the block.

If a block is given with a cmd_string of “-’’, the block will be run in two separate processes: once in the parent, and once in a child. The parent process will be passed the pipe object as a parameter to the block, the child version of the block will be passed nil, and the child‘s standard in and standard out will be connected to the parent through the pipe. Not available on all platforms.

3. Use POpen4 gem

status = POpen4::popen4( cmd_ ) do |stdout, stderr, stdin|
    stdout.each do |line|
      puts line
puts status.exitstatus

In order for the above code to work, you need to install POpen4 gem with the following command:

gem install POpen4

POpen4 provides the Rubyist a single API across platforms for executing a command in a child process with handles on stdout, stderr, stdin streams as well as access to the process ID and exit status. It does very little other than to provide an easy way to use either Ara Howard’s Open4 library or the win32-popen3 library by Park Heesob and Daniel Berger depending on your platform and without having to code around the slight differences in their APIs.

POpen4 is very good because you can very easily get the exit status. If the program/command is running successfully, it will return 0, if there is any exception happening than it will return exit status 1. And if your program/command has control to either exit 0 or exit 1. All these exit status will be captured. However, both IO.popen and POpen4 can’t capture the output in realtime. The whole output stream is buffered and is only available when the command has finished running. Unless the running command/program flush the output every time it output something. But at most of time, we don’t have any control over the running command/program. That’s why you should take a look at the following option.

3. Use pty

require "pty"
      PTY.spawn( cmd_ ) do |r, w, pid|
          r.each { |line| print line;}       
       rescue Errno::EIO         
   rescue PTY::ChildExited => e
      puts "The child process exited!"

Pty is pseudo terminal and is only available under *.nix System. If you are using it under windows, you should see the error : No such file to load pty. So far, i didn’t find any alternative for pty in windows. Pty acts as if someone is running the command in a terminal, so the output is immediately flushed out. Bad thing with pty is that it behaves quite strange, sometimes, it will just jumps to the ChildExited exception even when you look at the output of the running command, everything has finished running. E.g. recently I have a rake task that zip a bunch of files from a certain folder. The zip file is successfully zipped, but it will go to the exception and print “The child process exited”. I still haven’t figure out what’s wrong with this and how do I capture the exit status correctly. If in the running command/program, i do exit 1. The pty won’t capture this exit status. Instead, it will always has the exit status of 0.

So depends on your purpose, if you wanna see realtime output, you can use pty. If realtime output is not that important to you, i recommend using POpen4. It runs very nicely.

9 Responses to “Ruby run a command and capture output in realtime”

  1. Fabian says:

    Nice Article! Helped a lot!

  2. Lau says:

    very useful blog

  3. Pieter says:

    Thanks Shanison, this helped me to decide which route to take! Just for info, I’m going the PTY route :)

  4. Sam Livingston-Gray says:

    Thanks for posting this; somehow I didn’t know about Kernel#system, and turns out it’s all I needed. :)

  5. Alexey Glukhov says:

    I would move inner begin..rescue clause to the topmost begin block since exceptions bubble up. A bit shorter and less nested code, easier to read.

    • shanison says:

      Thanks for the idea. Yes, it is possible to totally remove the inner begin block. I just leave it there to demonstrate what kind of exception will be thrown, so anyone who is interested to do something special to that exception can put the codes over there.

Leave a Reply

Your email address will not be published. Required fields are marked *