Question

How to Call Shell Commands from Ruby and Capture Output

rubyshellinterop

Question

How can I run shell commands from inside a Ruby program? Also, how can I capture the output of those commands and use it inside Ruby?

Short Answer

By the end of this page, you will understand the main ways Ruby can execute shell commands, how to capture standard output, how to detect success or failure, and when to use safer options such as system, backticks, %x, and Open3.

Concept

Ruby can launch external programs such as shell commands, scripts, and command-line tools. This is useful when your Ruby code needs to interact with the operating system or reuse existing tools.

Common examples include:

  • listing files with ls
  • running git commands
  • calling image-processing tools
  • executing deployment scripts
  • reading output from utilities like whoami or date

The important idea is that Ruby and the shell are separate environments:

  • Ruby runs your application logic
  • The shell runs system commands
  • Ruby can start a command, wait for it, and optionally capture its output

Ruby provides several ways to do this, and each one has a different purpose:

  • system runs a command and returns true or false depending on success
  • backticks `command` run a command and return its standard output as a string
  • %x(command) is another form of backticks
  • Open3 gives more control and can capture standard output, standard error, and exit status separately

This matters in real programming because external commands often produce useful data, but they can also fail, hang, or behave differently across operating systems. Choosing the right Ruby method helps you write safer and clearer code.

Mental Model

Think of Ruby as a manager and shell commands as workers.

  • If Ruby just wants a worker to do a task and report whether it succeeded, use system.
  • If Ruby wants the worker to bring back a note with the result, use backticks or %x.
  • If Ruby wants a full report with output, errors, and status, use Open3.

Another way to picture it:

  • system = press a button and check whether the machine worked
  • backticks = press a button and collect the printed receipt
  • Open3 = collect the receipt, the error log, and the final status code
Take Quiz

Syntax and Examples

Basic ways to run shell commands in Ruby

1. system

Use system when you want to run a command and mainly care whether it succeeds.

success = system("ls")
puts success
  • The command runs in the shell.
  • Output is printed directly to the terminal.
  • The return value is usually true for success and false for failure.

2. Backticks

Use backticks when you want the command output as a Ruby string.

output = `whoami`
puts output

Example output:

alice

The value stored in output includes the command's standard output.

3. %x()

This is another syntax for backticks.

output = %x(date)
puts output

This is useful if backticks are harder to read in a longer string.

4. Open3.capture3

Use this when you want output, error output, and exit status separately.

require "open3"

stdout, stderr, status = Open3.capture3("ls missing_file")

puts "STDOUT: #{stdout}"
puts "STDERR: #{stderr}"
puts "SUCCESS?: #{status.success?}"
puts "EXIT CODE: #{status.exitstatus}"

This is often the best choice when building robust scripts.

Example: capture a command result and use it in Ruby

current_user = `whoami`.strip
puts "Logged in as: #{current_user}"

Why strip?

Many shell commands return output ending with a newline. strip removes that extra whitespace.

Safer argument passing

Instead of building a shell string manually, Ruby can pass arguments directly.

system("ls", "-l", "/tmp")

This is usually safer than:

system("ls -l /tmp")

Passing separate arguments avoids many quoting and escaping problems.

Step by Step Execution

Example

output = `echo Hello`
message = output.strip
puts message

What happens step by step

1. Ruby sees the backticks

output = `echo Hello`

Ruby runs the shell command echo Hello.

2. The shell executes the command

The command prints this text to standard output:

Hello

Usually there is a newline at the end.

3. Ruby captures the output

The captured string becomes the value of output:

"Hello\n"

4. strip removes extra whitespace

message = output.strip

Now message is:

"Hello"

5. Ruby prints the cleaned result

puts message

The final output shown in the terminal is:

Hello

Another trace with success checking

success = system("ruby", "-v")
puts success

Step by step:

  1. Ruby starts the ruby -v command.
  2. The command prints the Ruby version directly to the terminal.
  3. If the command exits successfully, success becomes true.
  4. puts success prints true.

This example shows the key difference:

  • system shows output directly
  • backticks return output to Ruby

Real World Use Cases

Practical uses

Running Git commands

A Ruby deployment script might check the current branch:

branch = `git branch --show-current`.strip

Calling system tools

A script may need the hostname of the machine:

hostname = `hostname`.strip

Processing files with external tools

Ruby might call tools like ffmpeg, convert, or tar.

system("tar", "-czf", "backup.tar.gz", "project_folder")

Automation scripts

A Ruby script can gather system information and generate a report.

disk_usage = `df -h`.strip
puts disk_usage

CI/CD and deployment

Build scripts often run commands such as:

  • bundle install
  • git pull
  • rake db:migrate

Admin and maintenance tasks

Ruby can be used for:

  • restarting services
  • checking logs
  • cleaning temporary files
  • running scheduled jobs
Take Quiz

Real Codebase Usage

How developers use this in real projects

Guard clauses for failed commands

Developers often stop early if a required command fails.

unless system("git", "fetch")
  abort("Failed to fetch latest changes")
end

This keeps scripts simple and clear.

Capturing output for validation

A program might check a command result before continuing.

ruby_version = `ruby -v`.strip
abort("Ruby is not installed") if ruby_version.empty?

Using Open3 for error handling

When stderr matters, Open3 is common.

require "open3"

stdout, stderr, status = Open3.capture3("git status --short")
abort(stderr) unless status.success?
puts stdout

Avoiding shell interpolation with user input

In production code, developers avoid unsafe string building.

filename = "report.txt"
system("cat", filename)

This is better than interpolating directly into one shell string.

Logging command execution

Real codebases often log commands before running them.

command = ["ls", "-l"]
puts "Running: #{command.join(' ')}"
system(*command)

Wrapping commands in helper methods

Teams often create small helper functions.

require "open3"

def run_command(*args)
  stdout, stderr, status = Open3.capture3(*args)
  {
    stdout: stdout,
    stderr: stderr,
    success: status.success?,
    exit_code: status.exitstatus
  }
end

result = run_command("ruby", "-v")
puts result[:stdout] if result[:success]

Common Mistakes

1. Expecting system to return command output

Broken example:

output = system("whoami")
puts output

Why this is wrong:

  • system returns true or false
  • it does not return the command output

Correct version:

output = `whoami`
puts output

2. Forgetting to remove the trailing newline

name = `whoami`
puts "User: #{name}!"

This may print awkward formatting because name often contains \n.

Better:

name = `whoami`.strip
puts "User: #{name}!"

3. Ignoring command failure

output = `some_missing_command`
puts output

A command can fail silently from your program's point of view unless you check status.

Better approach:

output = `some_missing_command`
status = $?
puts status.success?

4. Building unsafe command strings

Broken example:

filename = params[:filename]
system("cat #{filename}")

If filename comes from a user, this can be dangerous.

Safer:

filename = params[:filename]
system("cat", filename)

5. Forgetting stderr exists

Backticks capture standard output, not standard error in a structured way.

If you need both stdout and stderr separately, prefer Open3.capture3.

require "open3"
stdout, stderr, status = Open3.capture3("ls missing_file")

6. Assuming shell commands are portable

Commands like ls, grep, and df may not work the same way on every operating system.

Avoid this mistake by:

  • checking the target environment
  • using Ruby's standard library when possible
  • documenting system requirements

Comparisons

Common Ruby options for running shell commands

MethodReturnsShows output in terminalCaptures stdoutCaptures stderr separatelyBest for
system("cmd")true/falseYesNoNoRun a command and check success
`cmd`StringNo, captured insteadYesNoGet command output as a string
%x(cmd)StringNo, captured insteadYesNoSame as backticks, alternate syntax
Open3.capture3("cmd")stdout, stderr, statusNo, captured insteadYesYesRobust scripts and error handling

system vs backticks

FeaturesystemBackticks
Main purposeExecute a commandExecute and capture output
Return valueBoolean-like success resultOutput string
Output destinationTerminalRuby variable
Good forTasks, installers, scriptsReading command results

Shell string vs argument list

StyleExampleNotes
Shell stringsystem("ls -l /tmp")Simpler to type, but quoting can be tricky
Argument listsystem("ls", "-l", "/tmp")Safer and clearer for dynamic values

When to use which

  • Use system if you only need to know whether the command worked.
  • Use backticks or %x if you need the output as a string.
  • Use Open3 if you need proper error handling and both output streams.
Take Quiz

Cheat Sheet

Quick reference

Run a command and check success

success = system("ls")

Run a command and capture output

output = `whoami`
output = output.strip

Alternative to backticks

output = %x(date)

Capture stdout, stderr, and status

require "open3"
stdout, stderr, status = Open3.capture3("ls missing_file")

Check the last exit status

output = `ls`
puts $?.success?
puts $?.exitstatus

Safer argument passing

system("ls", "-l", "/tmp")

Rules to remember

  • system returns success, not command output
  • backticks return stdout as a string
  • command output often ends with \n, so use strip
  • use Open3 when stderr and exit status matter
  • prefer argument lists over interpolated shell strings for safety

Edge cases

  • some commands behave differently across platforms
  • missing commands may fail even if Ruby code itself is valid
  • external commands can be slow, so avoid calling them repeatedly without need

FAQ

How do I capture shell command output in Ruby?

Use backticks or %x() if you want the command's standard output as a string.

What is the difference between system and backticks in Ruby?

system runs a command and returns whether it succeeded. Backticks run a command and return its output.

How do I get both stdout and stderr from a command in Ruby?

Use Open3.capture3 from Ruby's standard library.

How can I check if a shell command succeeded in Ruby?

With system, check the returned value. With backticks or Open3, inspect the exit status using $? or the returned status object.

Should I use shell commands or pure Ruby methods?

If Ruby's standard library can do the job, that is often more portable and easier to test. Use shell commands when you need existing system tools.

Is it safe to build commands with string interpolation?

Not always. If the values come from users or external input, prefer passing arguments separately to avoid shell injection issues.

Why does captured command output contain a newline?

Many shell commands print a trailing newline. Use .strip or .chomp to remove it.

Take Quiz

Mini Project

Description

Build a small Ruby command runner that executes a system command, captures its output, and reports whether it succeeded. This demonstrates the difference between output, errors, and exit status in a practical script.

Goal

Create a Ruby script that runs a command entered in code, prints stdout and stderr separately, and shows the exit code.

Requirements

  • Use Ruby and the open3 standard library.
  • Run at least one external command from the script.
  • Capture standard output, standard error, and exit status.
  • Print a clear success or failure message.
  • Show the numeric exit code.
Take Quiz

Keep learning

Related questions

View more