Ruby Language Operating System or Shell commands Recommended ways to execute shell code in Ruby:


Open3.popen3 or Open3.capture3:
Open3 actually just uses Ruby's spawn command, but gives you a much better API.


Popen3 runs in a sub-process and returns stdin, stdout, stderr and wait_thr.

require 'open3'
stdin, stdout, stderr, wait_thr = Open3.popen3("sleep 5s && ls")
puts "#{} #{} #{wait_thr.value.exitstatus}"


require 'open3'
cmd = 'git push heroku master'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  puts "stdout is:" +
  puts "stderr is:" +

will output: stdout is: stderr is:fatal: Not a git repository (or any of the parent directories): .git


require 'open3'
cmd = 'ping'
Open3.popen3(cmd) do |stdin, stdout, stderr, wait_thr|
  while line = stdout.gets
    puts line

will output:

Pinging [] with 32 bytes of data:
Reply from bytes=32 time=16ms TTL=54
Reply from bytes=32 time=10ms TTL=54
Reply from bytes=32 time=21ms TTL=54
Reply from bytes=32 time=29ms TTL=54
Ping statistics for
Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),
Approximate round trip times in milli-seconds:
Minimum = 10ms, Maximum = 29ms, Average = 19ms


require 'open3'

stdout, stderr, status = Open3.capture3('my_funky_command', 'and', 'some', 'argumants')
if status.success?
  # command completed successfully, do some more stuff
  raise "An error occured"


Open3.capture3('/some/binary with some args')  

Not recommended though, due to additional overhead and the potential for shell injections.

If the command reads from stdin and you want to feed it some data:

Open3.capture3('my_funky_command', stdin_data: 'read from stdin')  

Run the command with a different working directory, by using chdir:

Open3.capture3('my_funky_command', chdir: '/some/directory')