Building CLI Tools in R

· 4 min read · Updated March 18, 2026 · intermediate
r cli argparse optparse

Building command-line interfaces (CLIs) in R lets you automate tasks, create reusable scripts, and integrate R into larger pipelines. This guide covers the main approaches: argparse, optparse, Rscript with shebangs, and making your scripts executable.

Installing CLI Packages

Two packages dominate R CLI development: argparse and optparse. Both are available from CRAN.

# Install both packages
install.packages(c("argparse", "optparse"))

Using argparse

The argparse package provides a Python-inspired interface. It’s the most feature-rich option with support for positional arguments, optional flags, types, and automatic help generation.

library(argparse)

# Create the parser
parser <- ArgumentParser(description = "Process data files")

# Add positional argument
parser$add_argument("input_file", 
                    help = "Path to input CSV file")

# Add optional arguments
parser$add_argument("--output", 
                    default = "output.csv",
                    help = "Path to output file [default: %(default)s]")

parser$add_argument("--filter",
                    help = "Filter column name")

parser$add_argument("--verbose",
                    action = "store_true",
                    help = "Print extra output")

# Parse the arguments
args <- parser$parse_args()

# Use the arguments
if (args$verbose) {
  cat("Processing:", args$input_file, "\n")
}

# Your processing logic here
data <- read.csv(args$input_file)

if (!is.null(args$filter)) {
  data <- data[data[[args$filter]] > 0, ]
}

write.csv(data, args$output, row.names = FALSE)

Run it:

Rscript process_data.R data.csv --output result.csv --verbose

argparse Key Features

  • Positional arguments: Required inputs specified by position
  • Optional arguments: Flags and options with --prefix
  • Type checking: Automatic conversion to integer, numeric, or logical
  • Choices: Restrict values to a predefined set
  • Automatic help: --help generates usage information
parser$add_argument("--format",
                    choices = c("csv", "tsv", "json"),
                    default = "csv",
                    help = "Output format [default: %(default)s]")

Using optparse

The optparse package offers a similar interface but uses a functional style. It’s slightly simpler and works well for basic use cases.

library(optparse)

# Define options
option_list <- list(
  make_option(c("-v", "--verbose"),
              action = "store_true",
              default = TRUE,
              help = "Print extra output [default]"),
  
  make_option(c("-q", "--quiet"),
              action = "store_false",
              dest = "verbose",
              help = "Print little output"),
  
  make_option(c("-c", "--count"),
              type = "integer",
              default = 5,
              help = "Number of samples [default: %default]",
              metavar = "number")
)

# Parse
opt <- parse_args(OptionParser(option_list = option_list))

# Use options
if (opt$verbose) {
  cat("Generating", opt$count, "samples\n")
}

# Generate random samples
samples <- rnorm(opt$count)
cat(samples, "\n")

Run it:

Rscript generate_samples.R -c 10
Rscript generate_samples.R -q -c 100

Shebangs and Executable Scripts

Make R scripts executable directly from the shell using a shebang line:

#!/usr/bin/env Rscript --vanilla

# Hello World CLI in R
args <- commandArgs(trailingOnly = TRUE)

if (length(args) == 0) {
  cat("Usage: hello.R NAME\n")
  quit(status = 1)
}

cat("Hello,", args[1], "!\n")

Save as hello.R and make it executable:

chmod +x hello.R
./hello.R World

The --vanilla flag prevents loading .Rprofile or saving workspaces, making scripts more predictable.

Finding Rscript

Use #!/usr/bin/env Rscript for portability across systems. It searches the PATH for R. On some systems, you may need the full path:

#!/usr/bin/local/Rscript
# or
#!/opt/R/bin/Rscript

Exit Codes

Proper CLI tools communicate success or failure through exit codes:

# Success (default)
quit(status = 0)

# Failure
quit(status = 1)

# Error with message
stop("Something went wrong")

Bash can check exit codes:

./script.R || echo "Script failed"

Complete Example: Data Processor

Here’s a more complete CLI tool combining argparse with real functionality:

#!/usr/bin/env Rscript --vanilla

library(argparse)

parser <- ArgumentParser(description = "Filter and summarize CSV data")

parser$add_argument("file",
                    help = "Input CSV file")

parser$add_argument("--group-by",
                    help = "Column to group by")

parser$add_argument("--sum",
                    help = "Column to sum",
                    default = NULL)

parser$add_argument("--mean",
                    help = "Column to average",
                    default = NULL)

parser$add_argument("--output",
                    "-o",
                    default = NULL,
                    help = "Output file (default: stdout)")

args <- parser$parse_args()

# Read data
data <- read.csv(args$file)

# Process
if (!is.null(args$group_by)) {
  if (!is.null(args$sum)) {
    result <- aggregate(data[[args$sum]],
                        by = list(group = data[[args$group_by]]),
                        FUN = sum)
    names(result) <- c(args$group_by, args$sum)
  } else if (!is.null(args$mean)) {
    result <- aggregate(data[[args$mean]],
                        by = list(group = data[[args$group_by]]),
                        FUN = mean)
    names(result) <- c(args$group_by, args$mean)
  }
} else {
  result <- data
}

# Output
if (is.null(args$output)) {
  print(result)
} else {
  write.csv(result, args$output, row.names = FALSE)
  cat("Wrote", args$output, "\n")
}

Run it:

chmod +x process.R
./process.R sales.csv --group-by region --sum revenue --output summary.csv

Choosing Between argparse and optparse

Featureargparseoptparse
Positional argsYesLimited
Automatic helpYesManual
Python argparse styleYesNo
Simpler syntaxNoYes

Recommendation: Use argparse for new projects—it handles more cases and produces cleaner help messages.

Summary

  • Use argparse or optparse for argument parsing
  • Use shebangs (#!/usr/bin/env Rscript) for executable scripts
  • Make scripts executable with chmod +x
  • Return proper exit codes for shell integration
  • argparse is recommended for new CLI projects

See Also