HOWTO execute shell commands with Ruby Open3

Learn how to execute shell commands with Ruby’s Open3 module—an alternative to backticks which supports input and error output.


Executing a command

Use the Open3.capture2 method if you just need to execute a command and capture its output, and the stdin_data option for writing data to standard input. For example:

require 'open3'

script = '<?php echo "Hello from PHP!" ?>'

stdout, status = Open3.capture2('php', stdin_data: script)

puts stdout

This method will return both the output of the command, and the process status. For capturing standard error as well as standard output, use the Open3.capture3 method.

Checking the process status

Open3 includes the process status in the return values, so remember to assign the correct number of variables for the method you’re calling, and check the status to make sure the command executed correctly and exited without any errors. For example:

require 'open3'

stdout, stderr, status = Open3.capture3('python -c "puts"')

if status.success?
  puts stdout
else
  abort 'error: could not execute command'
end

The status value is of the same Process::Status class that backticks uses, with an #exitstatus method for checking the command’s exit code.

Escaping arguments

With Open3 you need to use string interpolation or concatenation to include arguments:

require 'open3'
require 'shellwords'

path = 'Ruby Mechanize Handbook.pdf'

stdout, status = Open3.capture2("md5 -q #{Shellwords.escape(path)}")

puts stdout

Escaping isn’t handled for you, so if you have any arguments which need to be escaped you’ll need to escape them yourself with the Shellwords module.

Reading and writing line by line

Open3 also has #popen2 and #popen3 methods for more complex use-cases where you need to read and write line by line interactively. For example:

require 'open3'

stdin, stdout, waiter = Open3.popen2('tee')

3.times do
  stdin.puts('Chunky bacon!')

  puts stdout.gets
end

stdin.close

stdout.close

status = waiter.value

In addition to returning IO streams these methods also return a Process::Waiter object, which you can use to get the process status once the command has finished executing.

Comparison to alternatives

Using the Open3 module instead of executing commands with backticks is the obvious choice when you need to write input to the command or capture error output.

If you find yourself reaching for more abstraction to add features like automatic argument escaping or command logging, or you just find the names and signatures of the different Open3 methods confusing, the tty-command gem might be a better choice.