allow repeated instances of same flag/option on command line
greg-minshall opened this issue · 3 comments
many Unix commands allow the same option to be repeated on the command line. for example, the more instances of the debug flag -d
, the more verbose the debugging output. or, for the C compiler, each instance of -I
specifies a different directory in which to look for #include
files.
also, for some applications, it's important that the options be "presented" to the program in the same order in which they appear on the command line.
it would be nice if optparse
supported this style. here's an example, using callback:
require(optparse)
cb <- function(s4, lflag, val, ps4) { cat(sprintf("cb: %s %s\n", lflag, val)) }
options <- list(
make_option(c("-i", "--include"), action="callback", type="character",
callback=cb, help="add an include directory (tree)")
)
p <- OptionParser(option_list=options)
## i should see two messages (invocations of cb()), but i only see one
args <- parse_args2(p, args=c("-i", "/usr/include", "-i", "/usr/X11/include"))
bonus points for extending the action
parameter with "append"
and "count"
actions so one doesn't always need to use callback
. (though the github's helpful "Similar issues" bit shows me #22 already asked for these.)
My answer from before still stands:
If a patch were submitted which doesn't break any of the package unit tests then I would likely accept one. Note that optparse is currently a wrapper of the R package getopt (which I didn't write but now maintain) which currently throws an error for options specified multiple times).
Note the upstream getopt
currently returns a named list of observed option (long) names and values which is an awkward structure for trying to implement those use cases.
Please note that if you don't mind a Python dependency there is also an R package argparse that is a wrapper of that Python package. In the python world optparse for several years has been deprecated in favor of argparse.
I also run into the same problem. Simultaneously I would like to keep optparse
as the option parser as it just works reliably without additional dependencies.
Thus I came up with a workaround, which can be useful for other people landing on this issue. The function combines all repeated arguments (type="character"
) into a single one with values glued with sep="|"
. The result can be parsed as usual and then results can be separated later.
combine.repeated.opt = function(obj, args, sep = "|") {
opts <- obj@options
opts <- opts[sapply(opts, function(x) x@type == "character")]
for (opt in opts) {
n <- length(args)
values <- character(0)
to.remove <- integer(0)
x <- opt@short_flag
if (!is.na(x)) {
pattern <- paste0("^",x,"$")
i <- grep(pattern, args)
i <- i[i+1 <= n]
values <- c(values, args[i+1])
to.remove <- c(to.remove, c(i,i+1))
}
x <- opt@long_flag
pattern <- paste0("^",x,"$")
i <- grep(pattern, args)
i <- i[i+1 <= n]
values <- c(values, args[i+1])
to.remove <- c(to.remove, c(i,i+1))
pattern <- paste0("^",x,"=")
i <- grep(pattern, args)
values <- c(values,sub(pattern,"",args[i]))
to.remove <- c(to.remove, c(i))
if (length(values) > 1) {
i = min(to.remove)-1
args = args[-to.remove]
n = length(args)
newargs = c(opt@long_flag, paste0(values, collapse=sep))
args = c(
args[seq_len(i)],
newargs,
args[seq_len(n-i)+i]
)
}
}
args
}
Example of usage:
require(optparse)
options <- list(
make_option(c("-i", "--include"), action="store", type="character", help="add an include directory"),
make_option(c("-o", "--output"), action="store", type="character", help="output file")
)
# args <- commandArgs()
args <- c("program", "-i", "/usr/include", "-o", "file", "-i", "/usr/X11/include")
parser <- OptionParser(option_list=options)
args <- combine.repeated.opt(parser, args)
args <- parse_args2(parser, args = args)
includes <- strsplit(args$options$include,split="[|]")[[1]]
This workaround doesn't affect any usage without repeated options.
@trevorld If you think you'd like this (or similar) solution in the package I can contribute it trough a pull-request. In the package it could be implemented as an option to make_option
so it can be enabled per-option.
- @llaniewski thanks for sharing your solution to this problem. However I don't think I'd want this particular solution in a PR. I do hope though that it may help people tackling this problem.
- I would prefer a PR that instead solves #22 (i.e. directly add support for
--count
and/or--append
actions). As noted in that issue this either requires changes to the{getopt}
module or implementing/copying our own low-level command line parser in{optparse}
that supports this without breaking any existing functionality. If anyone wants to open such an involved PR it would be good to discuss your target approach with me in issues before-hand to ensure the changes would be a good fit for the project(s). - I may also note that my
{argparse}
package does support counting repeated instances of a flag/option...