rguides

system

system(command, intern = FALSE, ignore.stdout = FALSE, ignore.stderr = FALSE, wait = TRUE, input = NULL, show.output.on.console = TRUE, minimized = FALSE, invisible = TRUE, timeout = 0)

The system() function runs an operating system command from within R and returns the command’s exit status or captured output. It is a base R function available on all platforms. While system2() offers a more portable and flexible interface, system() remains widely used for simple command invocations.

Syntax

system(command, intern = FALSE,
       ignore.stdout = FALSE, ignore.stderr = FALSE,
       wait = TRUE, input = NULL,
       show.output.on.console = TRUE, minimized = FALSE,
       invisible = TRUE, timeout = 0)

Parameters

ParameterTypeDefaultDescription
commandcharacterRequiredThe OS command to execute, as a character string
internlogicalFALSEIf TRUE, capture the command’s output as an R character vector
ignore.stdoutlogicalFALSEIf TRUE, discard output written to stdout
ignore.stderrlogicalFALSEIf TRUE, discard output written to stderr
waitlogicalTRUEIf TRUE, wait for the command to finish before continuing
inputcharacterNULLA character vector piped to the command’s standard input
show.output.on.consolelogicalTRUEWindows-only: display output on the Rgui console
minimizedlogicalFALSEWindows-only: launch the command minimized
invisiblelogicalTRUEWindows-only: keep the command window hidden
timeoutnumeric0Maximum seconds to wait before terminating the command

Return value

The return type depends on the intern argument:

  • intern = FALSE: Returns an integer exit code. 0 means success. 127 means the command could not be run. The value is invisible, so you must print it explicitly to see it.

  • intern = TRUE: Returns a character vector containing the command’s output, one element per line. If the command exits with a non-zero status, the result includes a warning and the exit code is stored in the status attribute.

Platform differences

The behavior of system() differs significantly between Unix-like systems and Windows.

Unix/Linux/macOS: The command is passed to a shell (typically /bin/sh), so you can use shell features like pipes, redirection, and multiple commands separated by ;.

Windows: The command runs directly without a shell. Use shell() if you need shell features on Windows.

This means the following works on Unix but not Windows:

# Unix: reads stderr alongside stdout
system("grep 'warning' myfile.log 2>&1", intern = TRUE)

On Windows, you need system2() or shell() for equivalent functionality. The platform split is the single most common source of portability surprises with system() and is the main reason system2() was added to R in version 2.12.0. When your script needs to run on both operating systems, system2() is almost always the safer choice because it constructs the command internally rather than relying on shell parsing.

Capturing command output

Use intern = TRUE to capture output as an R character vector instead of printing it to the console. Switching from intern = FALSE to intern = TRUE fundamentally changes what system() returns: instead of an invisible integer exit code you get a character vector that you can process with standard R string manipulation functions like grep() or strsplit().

# Capture the output of the ls command
result <- system("ls -la", intern = TRUE)
class(result)
# [1] "character"

# First few lines of output
head(result)
# [1] "total 24 drwxr-xr-x  5 user user 4096 Mar 24 04:00 ."         
# [2] "drwxr-xr-x  3 user user 4096 Mar 24 04:00 .."
# [3] "-rw-r--r--  1 user user  220 Mar 23 13:00 .bashrc"

When you capture output with intern = TRUE, you sacrifice the direct exit code. The status is still accessible as an attribute of the result vector via attr(result, "status"), but it takes an extra step. For automation scripts where you need to gate subsequent actions on command success or failure, swapping back to intern = FALSE and inspecting the integer return value is the more idiomatic pattern in base R.

Checking command exit status

When intern = FALSE, system() returns the exit code. Always check this for errors in production scripts.

# Check if a command succeeded
exit_code <- system("mkdir -p output/data")
if (exit_code != 0) {
  warning("Failed to create output directory")
}

# Check if an executable exists
cmd_exists <- function(cmd) {
  system(cmd, intern = FALSE, ignore.stdout = TRUE, ignore.stderr = TRUE) == 0
}

cmd_exists("python3")
# [1] TRUE

cmd_exists("nonexistent_command_xyz")
# [1] FALSE

Beyond running commands and reading their results, you can pipe R data directly into shell commands without ever touching the file system. This is especially handy in data pipelines where you have a character vector in memory and want to feed it through a Unix filter like wc, grep, or sort. The input argument turns system() into a two-way bridge between R objects and command-line tools.

Passing input to commands

Use the input argument to send data to a command’s standard input.

# Send text to a command's stdin
lines <- c("first line", "second line", "third line")
system("wc -l", input = lines)
# 3

When you pipe data into a command, R blocks until that command finishes processing, which is the default wait = TRUE behaviour. But sometimes you want R to fire off a command and keep working, especially for long-running processes like server startup or file downloads where the result isn’t needed immediately. The wait parameter governs this distinction between synchronous and asynchronous execution.

Running commands asynchronously

Set wait = FALSE to run a command in the background and return immediately.

# Launch a long-running process without blocking
system("sleep 60", wait = FALSE)

# The R session continues without waiting for sleep to finish
print("This prints immediately")
# [1] "This prints immediately"

While wait = FALSE avoids blocking the R session entirely, you often need a middle ground: letting a command run but with a firm deadline after which it gets killed. This is critical in scripts that call external APIs or network resources, where a hung connection can freeze your entire R process indefinitely. The timeout parameter gives you bounded blocking with an automatic safety cutoff.

Handling timeouts

Use the timeout argument to prevent commands from running indefinitely.

# If this takes more than 5 seconds, it will be killed
result <- system("curl -s https://httpbin.org/delay/10",
                 timeout = 5, intern = TRUE)
# Warning: running command 'curl -s https://httpbin.org/delay/10' had timeout 5
#attr(,"status")
#[1] 124

Exit status 124 indicates the command was terminated by timeout. In production pipelines, you also need to separate normal output from error messages. Shell redirection operators like 2>&1 let you merge stderr into stdout so both streams appear in the character vector you capture, while 2>/dev/null discards errors entirely when only the successful output matters.

Redirecting stderr

On Unix systems, you can capture stderr using shell redirection within the command string.

# Capture both stdout and stderr combined
combined <- system("echo 'out' && echo 'err' >&2", intern = TRUE)
combined
# [1] "out" "err"

Merging both streams into one vector is fine for quick debugging, but production scripts usually want clean stdout without error noise mixed in. Discarding stderr entirely is the right call when you’re parsing structured command output — think JSON or CSV — where an unrelated warning message would break your downstream parsing logic. The redirection pattern you choose depends entirely on whether error messages constitute signal or noise for your particular pipeline stage.

# Run a command and capture stdout, ignoring stderr
system("some_command 2>/dev/null", intern = TRUE)

Common mistakes

Assuming commands work the same across platforms. Shell features like pipes and redirection work on Unix but not Windows. For cross-platform scripts, prefer system2() with explicit argument handling.

Forgetting that return values are invisible. When intern = FALSE, the exit code is returned invisibly. You must wrap in print() or assign to a variable to see it.

Not checking exit codes in automation scripts. A non-zero exit code means the command failed. Ignoring this is a common source of silent failures in production R scripts.

Passing unquoted paths with spaces. On Unix, paths with spaces must be quoted in the command string, just as you would in a shell. The function does not do this automatically.

# Wrong - shell splits on spaces, so "ls My Documents" runs ls with
# two arguments: "My" and "Documents"
system("ls My Documents")

# Correct - use shQuote to properly quote the path
system(paste("ls", shQuote("My Documents")))

See also

  • readline(), read interactive user input from the console
  • Sys.sleep(), pause R execution for a given number of seconds
  • cat() — write output to the R console