Question
Ruby exec vs system vs backticks (%x): Differences, Return Values, and When to Use Each
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 programsystem: 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
execwill not run ifexecsucceeds - 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:
trueif the command exited successfullyfalseif it exited with a failure statusnilif 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.
execis like Ruby taking off its badge and letting the other worker take its place completely. Ruby is gone.systemis 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.
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 execis printedlsrunsAfter execis 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
-
Ruby prints:
Start -
Ruby runs this external command:
ruby -e 'puts 1 + 1' -
That external Ruby process prints:
2 -
The command finishes successfully.
-
system(...)returnstrue, soresultbecomestrue. -
$?.exitstatusis usually0, which means success. -
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
outputbecomes 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?
- Ruby prints
Start - Ruby is replaced by the new command
- The new command prints
2 - The original Ruby program does not continue
Endis 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, orrmin 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()
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
falsemeans the command ran but failednilmeans Ruby could not run the command at all
Comparisons
| Feature | exec | system | Backticks / %x() |
|---|---|---|---|
| Runs an external command | Yes | Yes | Yes |
| Ruby continues afterward | No, if successful | Yes | Yes |
| Returns command output | No | No | Yes |
| Returns success/failure directly | No | Yes | Not directly |
| Replaces current process | Yes | No | No |
| Useful for scripting flow | Rarely | Yes | Yes |
| Best for | Handoff to another program | Run and check success | Capture 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.
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, ornil - 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
systemdoes not give you command outputexecprevents later Ruby code from running if it succeedssystemreturnsnilif 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.
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
systemto 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
execat 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.
Keep learning
Related questions
Add a Default Value to an Existing Column in a Rails Migration
Learn how to add or change a default value on an existing database column using Rails Active Record migrations safely.
Calling a Class Method from an Instance in Ruby
Learn how to call a class method from an instance in Ruby using self.class, with examples, pitfalls, and practical usage patterns.
Calling an Overridden Monkey-Patched Method in Ruby
Learn how to call the original method when monkey patching in Ruby, including alias_method patterns, examples, pitfalls, and practical usage.