# argparse Command Line Argument Parsing

argparse is a command line argument parser inspired by Python’s “argparse” library. Use this with Rscript to write “#!”-shebang scripts that accept short and long flags/options and positional arguments, generate a usage statement, and set default values for options that are not specified on the command line.

In our working directory we have two example R scripts, named “example.R” and “display_file.R” illustrating the use of the argparse package.

bash$ ls

    display_file.R
    example.R

In order for a *nix system to recognize a “#!”-shebang line you need to mark the file executable with the `chmod` command, it also helps to add the directory containing your Rscripts to your path:

bash$ chmod ug+x display_file.R example.R

bash$ display_file.R example.R

Here is what “example.R” contains:

bash$ display_file.R example.R

    #!/usr/bin/env Rscript
    # Copyright 2012-2013 Trevor L Davis <trevor.l.davis@gmail.com>
    # Copyright 2008 Allen Day
    #
    #  This file is free software: you may copy, redistribute and/or modify it
    #  under the terms of the GNU General Public License as published by the
    #  Free Software Foundation, either version 2 of the License, or (at your
    #  option) any later version.
    #
    #  This file is distributed in the hope that it will be useful, but
    #  WITHOUT ANY WARRANTY; without even the implied warranty of
    #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    #  General Public License for more details.
    #
    #  You should have received a copy of the GNU General Public License
    #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    suppressPackageStartupMessages(library("argparse"))

    # create parser object
    parser <- ArgumentParser()

    # specify our desired options
    # by default ArgumentParser will add an help option
    parser$add_argument("-v", "--verbose", action="store_true", default=TRUE,
        help="Print extra output [default]")
    parser$add_argument("-q", "--quietly", action="store_false",
        dest="verbose", help="Print little output")
    parser$add_argument("-c", "--count", type="integer", default=5,
        help="Number of random normals to generate [default %(default)s]",
        metavar="number")
    parser$add_argument("--generator", default="rnorm",
        help = "Function to generate random deviates [default \"%(default)s\"]")
    parser$add_argument("--mean", default=0, type="double",
        help="Mean if generator == \"rnorm\" [default %(default)s]")
    parser$add_argument("--sd", default=1, type="double",
            metavar="standard deviation",
        help="Standard deviation if generator == \"rnorm\" [default %(default)s]")

    # get command line options, if help option encountered print help and exit,
    # otherwise if options not found on command line then set defaults,
    args <- parser$parse_args()

    # print some progress messages to stderr if "quietly" wasn't requested
    if ( args$verbose ) {
        write("writing some verbose output to standard error...\n", stderr())
    }

    # do some operations based on user input
    if( args$generator == "rnorm") {
        cat(paste(rnorm(args$count, mean=args$mean, sd=args$sd), collapse="\n"))
    } else {
        cat(paste(do.call(args$generator, list(args$count)), collapse="\n"))
    }
    cat("\n")

By default `argparse` will generate a help message if it encounters `--help` or `-h` on the command line. Note how `%(default)s` in the example program was replaced by the actual default values in the help statement that `argparse` generated.

bash$ example.R –help

    usage: example.R [-h] [-v] [-q] [-c number] [--generator GENERATOR] [--mean MEAN]
                     [--sd standard deviation]

    options:
      -h, --help            show this help message and exit
      -v, --verbose         Print extra output [default]
      -q, --quietly         Print little output
      -c number, --count number
                            Number of random normals to generate [default 5]
      --generator GENERATOR
                            Function to generate random deviates [default "rnorm"]
      --mean MEAN           Mean if generator == "rnorm" [default 0]
      --sd standard deviation
                            Standard deviation if generator == "rnorm" [default 1]

If you specify default values when creating your `ArgumentParser` then `argparse` will use them as expected.

bash$ example.R

    writing some verbose output to standard error...

    0.810728115060181
    1.09206646727706
    1.70378608013885
    0.201355429503964
    -0.00504818748381247

Or you can specify your own values.

bash$ example.R –mean=10 –sd=10 –count=3

    writing some verbose output to standard error...

    18.7400775590623
    15.9739894736902
    23.9043389427129

If you remember from the example program that `--quiet` had `action="store_false"` and `dest="verbose"`. This means that `--quiet` is a switch that turns the `verbose` option from its default value of `TRUE` to `FALSE`. Note how the `verbose` and `quiet` options store their value in the exact same variable.

bash$ example.R –quiet -c 4 –generator=“runif”

    0.0368707608431578
    0.252408046973869
    0.470769245177507
    0.661251338431612

If you specify an illegal flag then will print out a usage message and an error message and quit.

bash$ example.R –silent -m 5

    Error in .stop(output, "parse error:") : parse error:
    usage: example.R [-h] [-v] [-q] [-c number] [--generator GENERATOR] [--mean MEAN]
                     [--sd standard deviation]
    example.R: error: unrecognized arguments: --silent -m 5
    Calls: <Anonymous> -> parse_args_output -> .stop
    Execution halted

If you specify the same option multiple times then will use the value of the last option specified.

bash$ example.R -c 100 -c 2 -c 1000 -c 7

    writing some verbose output to standard error...

    0.521819620099909
    -1.00136095587277
    -0.516707752141829
    -0.136643072496566
    -0.000388440250166777
    -0.118239108707788
    1.28510853122564

`argparse` can also parse positional arguments. Below we give an example program `display_file.R`, which is a program that prints out the contents of a single file (the required positional argument, not an optional argument) and which accepts the normal help option as well as an option to add line numbers to the output.

bash$ display_file.R –help

    usage: display_file.R [-h] [-n] file

    positional arguments:
      file               File to be displayed

    options:
      -h, --help         show this help message and exit
      -n, --add_numbers  Print line number at the beginning of each line [default]

bash$ display_file.R –add_numbers display_file.R

    1 #!/usr/bin/env Rscript
    2 # Copyright 2012-2013 Trevor L Davis <trevor.l.davis@gmail.com>
    3 #
    4 #  This file is free software: you may copy, redistribute and/or modify it
    5 #  under the terms of the GNU General Public License as published by the
    6 #  Free Software Foundation, either version 2 of the License, or (at your
    7 #  option) any later version.
    8 #
    9 #  This file is distributed in the hope that it will be useful, but
    10 #  WITHOUT ANY WARRANTY; without even the implied warranty of
    11 #  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    12 #  General Public License for more details.
    13 #
    14 #  You should have received a copy of the GNU General Public License
    15 #  along with this program.  If not, see <http://www.gnu.org/licenses/>.
    16 suppressPackageStartupMessages(library("argparse"))
    17
    18 parser <- ArgumentParser()
    19 parser$add_argument("-n", "--add_numbers", action="store_true", default=FALSE,
    20     help="Print line number at the beginning of each line [default]")
    21 parser$add_argument("file", nargs=1, help="File to be displayed")
    22
    23 args <- parser$parse_args()
    24
    25 file <- args$file
    26
    27 if( file.access(file) == -1) {
    28     stop(sprintf("Specified file ( %s ) does not exist", file))
    29 } else {
    30     file_text <- readLines(file)
    31 }
    32
    33 if(args$add_numbers) {
    34     cat(paste(1:length(file_text), file_text), sep = "\n")
    35 } else {
    36     cat(file_text, sep = "\n")
    37 }

bash$ display_file.R non_existent_file.txt

    Error: Specified file ( non_existent_file.txt ) does not exist
    Execution halted

bash$ display_file.R

    Error in .stop(output, "parse error:") : parse error:
    usage: display_file.R [-h] [-n] file
    display_file.R: error: the following arguments are required: file
    Calls: <Anonymous> -> parse_args_output -> .stop
    Execution halted