rubyguides

Kernel#system

system([env,] command [, args...,]) -> true, false, or nil

The system method executes an external command as a subprocess and returns its exit status. It runs the command directly, without spawning a shell interpreter, making it more secure than using backticks or %x{} for commands with untrusted input. The method blocks the current thread until the command completes, then returns a value indicating success or failure.

Because it returns a simple status instead of captured text, system works well when the command output should stay visible in the terminal. That makes it useful for scripts, deployment tasks, and quick checks where the success flag is the main thing you need.

Unlike backticks, system does not capture the command’s stdout or stderr. The output goes directly to the parent’s stdout/stderr, which is useful when you want the subprocess output to appear in the terminal without interception.

That makes system a practical choice for command-line tools, deployment scripts, and small automation tasks where the output should stay visible to the user. It also keeps the call site simple because you can check the return value right away instead of parsing captured text.

The examples below move from the simplest success check to slightly more controlled command execution. That keeps the method’s main trade-off in view: you get a compact success flag instead of a captured string, which is exactly what many scripts need.

Syntax

system(command)
system(command, arg1, arg2, ...)
system(env_var: value, command, arg1, ...)

Parameters

ParameterTypeDefaultDescription
envHashnilOptional environment variables to set for the subprocess
commandStringThe command to execute. Can include spaces without shell interpretation
argsArraynilAdditional arguments passed as separate parameters to the command

Examples

basic usage

# Returns true if command succeeds (exit code 0)
system("ls")
# => true

# Returns false if command fails (non-zero exit code)
system("ls /nonexistent")
# => false

# Returns nil if command was not found
system("nonexistent_command")
# => nil

This trio covers the three return states in the simplest possible way. It is a good reminder that system answers the question “did the command succeed?” instead of handing back the output. When you only need a boolean or nil, the method is concise and clear; when you also want the command’s stdout, a different API is a better fit.

passing arguments safely

# Arguments are passed directly to the executable, not interpreted by a shell
# This avoids shell injection vulnerabilities
system("echo", "Hello", "World")
# => true
# Terminal output: Hello World

# Compare with shell interpretation (dangerous with user input)
system("echo $HOME")    # Shell expands $HOME
system("echo", "$HOME") # Prints literal "$HOME"

Passing arguments separately is the safer habit because Ruby hands them directly to the executable. That avoids shell expansion and keeps the command line easier to reason about when input comes from elsewhere. The multi-argument form also makes it clear at a glance that each token is a separate value, which helps during code review when someone needs to verify that the command is safe.

setting environment variables

# Set custom environment variables for just this subprocess
system({ "RAILS_ENV" => "test" }, "rake", "test")
# => true
# The parent process's RAILS_ENV is unchanged

The environment hash is a neat way to scope settings to one command without mutating the parent process. That makes it easy to run tests or deployment steps with the right environment variables and then move on. The hash is passed as the first argument and is not inherited by any subsequent system calls, so each invocation gets a clean environment tailored to its needs.

common patterns

running a script and checking success

if system("ruby script.rb")
  puts "Script succeeded"
else
  puts "Script failed with exit code: #{$?.exitstatus}"
end

Checking the return value directly is often the most readable pattern because the command and the decision sit in the same place. If the command fails, you can still inspect $? for the exit status without adding extra plumbing.

checking the exit status

system("rake", "db:migrate")
case $?.exitstatus
when 0 then puts "Success"
when 1 then puts "Failed"
when 2 then puts "Runtime error"
end

This pattern is useful when the exit code matters more than the output. It keeps the branching logic short and makes the reason for each outcome obvious at a glance.

differences from similar methods

MethodCaptures OutputShell InterpretationReturns
systemNoNotrue/false/nil
` `YesYesString
%x{}YesYesString
execNoNoDoes not return

errors

  • Errno::ENOENT: Raised if the command executable is not found or not executable.
  • ArgumentError: Raised if no command is provided.

When you use system, the biggest design choice is whether you care about the command output or only the success flag. If you need the output, another API may be a better fit. If success or failure is enough, system keeps the code compact and the behavior easy to explain.

See Also