Building CLI Tools in R
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:
--helpgenerates 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
| Feature | argparse | optparse |
|---|---|---|
| Positional args | Yes | Limited |
| Automatic help | Yes | Manual |
| Python argparse style | Yes | No |
| Simpler syntax | No | Yes |
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
- Base R: length() — Count elements in vectors
- Base R: cat() — Print output to console
- dplyr::filter() — Filter data frames