Question

Ruby exec vs system vs backticks (%x): Differences, Return Values, and When to Use Each

rubyexec

Question

In Ruby, what is the difference between exec, system, and %x() (or backticks) when running shell commands?

I understand that all three can execute terminal commands from Ruby, but I want to know why Ruby provides multiple ways to do this and when each one should be used.

Short Answer

By the end of this page, you will understand how Ruby runs external commands using exec, system, and backticks (%x()), and why they behave differently. You will learn which one replaces the current process, which one returns a success value, which one captures command output, and how to choose the right tool for real programs.

Concept

Ruby gives you multiple ways to run external commands because programs often need different kinds of behavior.

At first glance, exec, system, and backticks all seem similar because they all run a shell command. But they answer different needs:

  • exec: replace the current Ruby process with another program
  • system: run a command and wait for it to finish, returning whether it succeeded
  • backticks / %x(): run a command and capture its standard output as a string

These differences matter because when you run another program, you usually care about one or more of these questions:

  • Should Ruby continue running afterward?
  • Do I need the command's output?
  • Do I only care whether it succeeded?
  • Do I want to fully hand control over to another executable?

exec

exec does not start a child process and then return to Ruby in the usual way. Instead, it replaces the current Ruby process with the target command.

That means:

  • Ruby code after exec will not run if exec succeeds
  • The operating system process becomes the new program
  • This is useful when Ruby is only acting as a launcher

system

system runs a command, waits for it to finish, and returns:

  • true if the command exited successfully
  • false if it exited with a failure status
  • nil if the command could not be executed at all

This is useful when you care about success or failure, but not necessarily the command output.

Backticks / %x()

Backticks and %x() are equivalent. They run a command and return the command's standard output as a Ruby string.

This is useful when you want to read the result of a shell command inside your Ruby code.

Example use cases:

  • getting the current directory with pwd
  • reading a Git branch name
  • listing files

Why Ruby has all three

Because "run a command" can mean very different things:

  • "Replace this process with another one" → exec
  • "Run this command and tell me if it worked" → system
  • "Run this command and give me its output" → backticks / %x()

In real programming, choosing the right method makes your code clearer and avoids bugs such as expecting output from system or expecting Ruby to continue after exec.

Mental Model

Think of Ruby as a person sending work to another worker.

  • exec is like Ruby taking off its badge and letting the other worker take its place completely. Ruby is gone.
  • system is like Ruby asking another worker to do a job, waiting for the result, and hearing only whether it succeeded or failed.
  • backticks / %x() are like Ruby asking another worker to do a job and bring back a written report of the output.

So the main question is:

Do you want to replace yourself, check success, or capture output?

That choice determines which tool to use.

Take Quiz

Syntax and Examples

Core syntax

exec("ls")
system("ls")
output = `ls`
output = %x(ls)

Example: system

success = system("mkdir test_folder")
puts success

If the command succeeds, success will usually be true.

Example: backticks

files = `ls`
puts files

Here, the command output is stored in files as a string.

Example: exec

puts "Before exec"
exec("ls")
puts "After exec"

If exec succeeds:

  • Before exec is printed
  • ls runs
  • After exec is never printed

That is because the Ruby process has been replaced.

%x() and backticks are the same idea

These two are equivalent:

name = `whoami`
name = %x(whoami)

Both capture standard output.

Checking exit status

After system or backticks, Ruby stores process information in $?.

system("ls")
puts $?.exitstatus

Example with backticks:

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

$? is a Process::Status object representing the most recently finished child process.

Step by Step Execution

Consider this example:

puts "Start"
result = system("ruby -e 'puts 1 + 1'")
puts "Command success: #{result}"
puts "Exit status: #{$?.exitstatus}"
puts "End"

Step by step

  1. Ruby prints:

    Start
    
  2. Ruby runs this external command:

    ruby -e 'puts 1 + 1'
    
  3. That external Ruby process prints:

    2
    
  4. The command finishes successfully.

  5. system(...) returns true, so result becomes true.

  6. $?.exitstatus is usually 0, which means success.

  7. Ruby prints:

    Command success: true
    Exit status: 0
    End
    

Now compare with backticks:

puts "Start"
output = `ruby -e 'puts 1 + 1'`
puts "Captured: #{output.inspect}"
puts "Exit status: #{$?.exitstatus}"
puts "End"

What changes?

  • The command output is captured, not printed directly by Ruby code
  • output becomes something like:
    "2\n"
    
  • Ruby can now process that string

Finally, compare with exec:

puts "Start"
exec("ruby -e 'puts 1 + 1'")
puts "End"

What happens here?

  1. Ruby prints Start
  2. Ruby is replaced by the new command
  3. The new command prints 2
  4. The original Ruby program does not continue
  5. End is never printed

Real World Use Cases

When to use exec

Use exec when your Ruby program is only a wrapper that should hand control to another program.

Examples:

  • launching another executable from a setup script
  • replacing the current process in deployment scripts
  • starting a server process from a small bootstrap script

When to use system

Use system when you want to run a command and only need to know whether it succeeded.

Examples:

  • calling mkdir, cp, or rm in automation scripts
  • running tests from a Ruby script
  • executing a build command and stopping on failure
  • invoking CLI tools during deployment

When to use backticks / %x()

Use them when you need the command's output as data.

Examples:

  • reading the current Git commit hash
  • getting a list of files from a command
  • checking the hostname or username
  • parsing command-line tool output in a script
branch = `git branch --show-current`.strip
puts "Current branch: #{branch}"

A practical rule

  • Need to replace the process? Use exec
  • Need to check success? Use system
  • Need to capture output? Use backticks or %x()
Take Quiz

Real Codebase Usage

In real projects, developers often prefer the method that matches their intent as clearly as possible.

Common patterns

Guard clauses with system

unless system("bundle exec rspec")
  abort("Tests failed")
end

This is common in scripts and CI helpers.

Capture and clean output with backticks

ruby_version = `ruby -v`.strip
puts "Running on #{ruby_version}"

.strip is commonly used because command output often ends with a newline.

Bootstrap and handoff with exec

ENV["RACK_ENV"] ||= "production"
exec("bundle exec puma -C config/puma.rb")

This is useful when the current Ruby script should not remain as an extra process.

Exit status handling

Developers often inspect $? after system or backticks.

output = `git rev-parse --short HEAD`
abort("Git command failed") unless $?.success?
puts output

Validation before executing commands

Real code often validates input before building shell commands.

filename = "report.txt"
abort("Invalid filename") if filename.empty?
system("cat #{filename}")

However, constructing commands as strings can be risky if user input is involved. In production code, developers often prefer safer APIs that avoid shell interpolation when possible.

Why clarity matters

A reader should be able to look at the code and know immediately:

  • whether output is needed
  • whether the script continues afterward
  • whether only success/failure matters

That is exactly why these methods all exist separately.

Common Mistakes

1. Expecting system to return command output

Broken expectation:

files = system("ls")
puts files

files is not the listing. It is usually true or false.

Use backticks instead:

files = `ls`
puts files

2. Expecting code after exec to run

Broken code:

exec("ls")
puts "Done"

If exec works, puts "Done" never runs.

3. Forgetting that backticks include a trailing newline

name = `whoami`
puts name == "alice"   # often false

Why? Because name may actually be "alice\n".

Fix:

name = `whoami`.strip
puts name == "alice"

4. Ignoring command failure

output = `some_bad_command`
puts output

Backticks return output, but you should also check whether the command succeeded.

output = `some_bad_command`
unless $?.success?
  abort("Command failed")
end

5. Confusing command output with exit status

  • Output is text printed to standard output
  • Exit status is the success/failure code

These are different things.

6. Building shell commands unsafely

Broken pattern:

user_input = "file.txt; rm -rf /"
system("cat #{user_input}")

This can be dangerous if input comes from users.

Avoid inserting untrusted input directly into shell command strings.

7. Forgetting that nil from system is different from false

result = system("not_a_real_command")
p result
  • false means the command ran but failed
  • nil means Ruby could not run the command at all

Comparisons

FeatureexecsystemBackticks / %x()
Runs an external commandYesYesYes
Ruby continues afterwardNo, if successfulYesYes
Returns command outputNoNoYes
Returns success/failure directlyNoYesNot directly
Replaces current processYesNoNo
Useful for scripting flowRarelyYesYes
Best forHandoff to another programRun and check successCapture output

system vs backticks

system("date")   # prints the date, returns true/false/nil
`date`            # returns the date as a string

exec vs system

exec("date")     # Ruby process becomes `date`
system("date")   # runs `date`, then returns to Ruby

Backticks vs %x()

They are equivalent in behavior.

`date`
%x(date)

Use whichever is more readable in your codebase.

Take Quiz

Cheat Sheet

Quick reference

exec

exec("ls")
  • Replaces the current Ruby process
  • Does not return if successful
  • Use when handing off execution to another program

system

ok = system("ls")
  • Runs command and waits
  • Returns true, false, or nil
  • Good when you care about success/failure

Backticks / %x()

output = `ls`
output = %x(ls)
  • Runs command and captures standard output
  • Returns a string
  • Good when you need the command result as data

Exit status

system("ls")
puts $?.exitstatus
puts $?.success?

Common rule of thumb

  • Replace process → exec
  • Check success → system
  • Capture output → backticks / %x()

Important edge cases

  • Backticks usually include a trailing newline
  • system does not give you command output
  • exec prevents later Ruby code from running if it succeeds
  • system returns nil if the command cannot be started

FAQ

Why does Ruby have both system and backticks?

Because they solve different problems. system tells you whether a command succeeded, while backticks give you the command output as a string.

What is the difference between backticks and %x() in Ruby?

There is no meaningful difference in behavior. %x() is just another syntax for command substitution.

Does exec create a new process in Ruby?

exec replaces the current process with the target program. The Ruby program does not continue if exec succeeds.

How do I capture shell command output in Ruby?

Use backticks or %x():

output = `pwd`

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

Use system or inspect $? after running a command:

ok = system("ls")
puts $?.success?

Why is my backtick result ending with \n?

Because most shell commands print a newline at the end of their output. Use .strip or .chomp to remove it.

When should I use exec instead of system?

Use exec when your Ruby script should completely hand control over to another program, not continue afterward.

Take Quiz

Mini Project

Description

Build a small Ruby script that runs a few shell commands in different ways so you can see the difference between system, backticks, and exec in practice. This project is useful because it mirrors real scripting tasks: checking whether commands succeed, capturing output for later use, and handing control off to another program.

Goal

Create a Ruby script that demonstrates success checking with system, output capture with backticks, and process replacement with exec.

Requirements

  • Use system to run a command and print whether it succeeded.
  • Use backticks or %x() to capture command output and display it.
  • Show the last exit status using $?.
  • Use exec at the end so it does not prevent earlier parts of the script from running.
  • Print clear labels so the behavior of each method is easy to compare.
Take Quiz

Keep learning

Related questions

View more