在 R 中通过命令行传递多个参数

问题描述 投票:0回答:7

我试图通过命令行将多个文件路径参数传递给 Rscript,然后可以使用参数解析器对其进行处理。最终我想要这样的东西

Rscript test.R --inputfiles fileA.txt fileB.txt fileC.txt --printvar yes --size 10 --anotheroption helloworld -- etc...

通过命令行传递,并在解析时将结果作为 R 中的数组

args$inputfiles =  "fileA.txt", "fileB.txt", "fileC.txt"

我尝试了几种解析器,包括 optparse 和 getopt,但它们似乎都不支持此功能。我知道 argparse 可以,但它目前不适用于 R 版本 2.15.2

有什么想法吗?

谢谢

r command-line arguments argparse optparse
7个回答
7
投票

您描述命令行选项的方式与大多数人期望的使用方式不同。通常,命令行选项将采用单个参数,而没有前面选项的参数将作为参数传递。如果一个参数需要多个项目(如文件列表),我建议使用 strsplit() 解析字符串。

这是一个使用 optparse 的示例:

library (optparse)
option_list <- list ( make_option (c("-f","--filelist"),default="blah.txt", 
                                   help="comma separated list of files (default %default)")
                     )

parser <-OptionParser(option_list=option_list)
arguments <- parse_args (parser, positional_arguments=TRUE)
opt <- arguments$options
args <- arguments$args

myfilelist <- strsplit(opt$filelist, ",")

print (myfilelist)
print (args)

以下是几个运行示例:

$ Rscript blah.r -h
Usage: blah.r [options]


Options:
    -f FILELIST, --filelist=FILELIST
        comma separated list of files (default blah.txt)

    -h, --help
        Show this help message and exit


$ Rscript blah.r -f hello.txt
[[1]]
[1] "hello.txt"

character(0)
$ Rscript blah.r -f hello.txt world.txt
[[1]]
[1] "hello.txt"

[1] "world.txt"
$ Rscript blah.r -f hello.txt,world.txt another_argument and_another
[[1]]
[1] "hello.txt" "world.txt"

[1] "another_argument" "and_another"
$ Rscript blah.r an_argument -f hello.txt,world.txt,blah another_argument and_another
[[1]]
[1] "hello.txt" "world.txt" "blah"     

[1] "an_argument"      "another_argument" "and_another"     

注意,对于strsplit,您可以使用正则表达式来确定分隔符。我建议您使用逗号或冒号来分隔列表:

myfilelist <- strsplit (opt$filelist,"[,:]")

6
投票

虽然当这个问题被问到时,它还没有在 CRAN 上发布,但现在已经有了 argparse 模块的 beta 版本,它可以做到这一点。它基本上是流行的同名 python 模块的包装器,因此您需要安装最新版本的 python 才能使用它。有关详细信息,请参阅安装说明。基本示例包括对任意长的数字列表进行求和,这些数字应该不难修改,因此您可以获取任意长的输入文件列表。

> install.packages("argparse")
> library("argparse")
> example("ArgumentParser")

4
投票

在你的脚本 test.R 的前面,你把这个:

args <- commandArgs(trailingOnly = TRUE)

hh <- paste(unlist(args),collapse=' ')
listoptions <- unlist(strsplit(hh,'--'))[-1]
options.args <- sapply(listoptions,function(x){
         unlist(strsplit(x, ' '))[-1]
        })
options.names <- sapply(listoptions,function(x){
  option <-  unlist(strsplit(x, ' '))[1]
})
names(options.args) <- unlist(options.names)
print(options.args)

获得:

$inputfiles
[1] "fileA.txt" "fileB.txt" "fileC.txt"

$printvar
[1] "yes"

$size
[1] "10"

$anotheroption
[1] "helloworld"

1
投票

在四处搜索并避免从下往上编写新包之后,我发现使用包 optparse 输入多个参数的最佳方法是用一个字符分隔输入文件,该字符很可能是非法包含在文件名中的(例如,冒号)

Rscript test.R --inputfiles fileA.txt:fileB.txt:fileC.txt etc...

文件名中也可以包含空格,只要空格被转义即可(optparse 将处理这个问题)

Rscript test.R --inputfiles file\ A.txt:file\ B.txt:fileC.txt etc...

最终,如果有一个包(可能是 optparse 的修改版本)能够支持问题和下面提到的多个参数,那就太好了

Rscript test.R --inputfiles fileA.txt fileB.txt fileC.txt

有人会认为这些琐碎的功能可以实现到广泛使用的包中,例如 optparse

干杯


1
投票
如果输入参数是相同长度的列表,@agstudy 的解决方案将无法正常工作。默认情况下,sapply 会将相同长度的输入折叠成矩阵而不是列表。修复方法很简单,只需在 sapply 解析参数时显式将 simple 设置为 false 即可。

args <- commandArgs(trailingOnly = TRUE) hh <- paste(unlist(args),collapse=' ') listoptions <- unlist(strsplit(hh,'--'))[-1] options.args <- sapply(listoptions,function(x){ unlist(strsplit(x, ' '))[-1] }, simplify=FALSE) options.names <- sapply(listoptions,function(x){ option <- unlist(strsplit(x, ' '))[1] }) names(options.args) <- unlist(options.names) print(options.args)
    

0
投票
我遇到了同样的问题,我开发的解决方法是在将输入命令行参数输入到

optparse

 解析器之前调整输入命令行参数,方法是使用替代分隔符(例如“管道”)将空格分隔的输入文件名连接在一起" 字符,不太可能用作文件名的一部分。

最后再次反转调整,使用

str_split()

 删除分隔符。

这是一些示例代码:

#!/usr/bin/env Rscript library(optparse) library(stringr) # ---- Part 1: Helper Functions ---- # Function to collapse multiple input arguments into a single string # delimited by the "pipe" character insert_delimiter <- function(rawarg) { # Identify index locations of arguments with "-" as the very first # character. These are presumed to be flags. Prepend with a "dummy" # index of 0, which we'll use in the index step calculation below. flagloc <- c(0, which(str_detect(rawarg, '^-'))) # Additionally, append a second dummy index at the end of the real ones. n <- length(flagloc) flagloc[n+1] <- length(rawarg) + 1 concatarg <- c() # Counter over the output command line arguments, with multiple input # command line arguments concatenated together into a single string as # necessary ii <- 1 # Counter over the flag index locations for(ij in seq(1,length(flagloc)-1)) { # Calculate the index step size between consecutive pairs of flags step <- flagloc[ij+1]-flagloc[ij] # Case 1: empty flag with no arguments if (step == 1) { # Ignore dummy index at beginning if (ij != 1) { concatarg[ii] <- rawarg[flagloc[ij]] ii <- ii + 1 } } # Case 2: standard flag with one argument else if (step == 2) { concatarg[ii] <- rawarg[flagloc[ij]] concatarg[ii+1] <- rawarg[flagloc[ij]+1] ii <- ii + 2 } # Case 3: flag with multiple whitespace delimited arguments (not # currently handled correctly by optparse) else if (step > 2) { concatarg[ii] <- rawarg[flagloc[ij]] # Concatenate multiple arguments using the "pipe" character as a delimiter concatarg[ii+1] <- paste0(rawarg[(flagloc[ij]+1):(flagloc[ij+1]-1)], collapse='|') ii <- ii + 2 } } return(concatarg) } # Function to remove "pipe" character and re-expand parsed options into an # output list again remove_delimiter <- function(rawopt) { outopt <- list() for(nm in names(rawopt)) { if (typeof(rawopt[[nm]]) == "character") { outopt[[nm]] <- unlist(str_split(rawopt[[nm]], '\\|')) } else { outopt[[nm]] <- rawopt[[nm]] } } return(outopt) } # ---- Part 2: Example Usage ---- # Prepare list of allowed options for parser, in standard fashion option_list <- list( make_option(c('-i', '--inputfiles'), type='character', dest='fnames', help='Space separated list of file names', metavar='INPUTFILES'), make_option(c('-p', '--printvar'), type='character', dest='pvar', help='Valid options are "yes" or "no"', metavar='PRINTVAR'), make_option(c('-s', '--size'), type='integer', dest='sz', help='Integer size value', metavar='SIZE') ) # This is the customary pattern that optparse would use to parse command line # arguments, however it chokes when there are multiple whitespace-delimited # options included after the "-i" or "--inputfiles" flag. #opt <- parse_args(OptionParser(option_list=option_list), # args=commandArgs(trailingOnly = TRUE)) # This works correctly opt <- remove_delimiter(parse_args(OptionParser(option_list=option_list), args=insert_delimiter(commandArgs(trailingOnly = TRUE)))) print(opt)
假设上面的文件名为

fix_optparse.R

,输出结果如下:

> chmod +x fix_optparse.R > ./fix_optparse.R --help Usage: ./fix_optparse.R [options] Options: -i INPUTFILES, --inputfiles=INPUTFILES Space separated list of file names -p PRINTVAR, --printvar=PRINTVAR Valid options are "yes" or "no" -s SIZE, --size=SIZE Integer size value -h, --help Show this help message and exit > ./fix_optparse.R --inputfiles fileA.txt fileB.txt fileC.txt --printvar yes --size 10 $fnames [1] "fileA.txt" "fileB.txt" "fileC.txt" $pvar [1] "yes" $sz [1] 10 $help [1] FALSE >
此方法的一个小限制是,如果任何其他参数有可能接受“管道”字符作为有效输入,那么这些参数将不会被正确处理。不过,我认为您可能可以开发该解决方案的稍微复杂的版本来正确处理这种情况。这个简单的版本在大多数情况下都有效,并说明了总体思路。


0
投票
刚刚遇到这个问题,幸运的是,

{argparser}包支持多值参数。 nargs

函数中有
add_argument()
参数,将其指定为
Inf
即可。

举个例子:

library(argparser) cli_args <- c("-s", 2, 3, 5) arg_parser("Test with multiple values") |> add_argument("--subject", "sub", type = "numeric", nargs = Inf) |> parse_args(cli_args) #> [[1]] #> [1] FALSE #> #> $help #> [1] FALSE #> #> $opts #> [1] NA #> #> $subject #> [1] 2 3 5

创建于 2023-10-10,使用 reprex v2.0.2

© www.soinside.com 2019 - 2024. All rights reserved.