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.