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

3yhwsihp  于 2023-10-13  发布在  其他
关注(0)|答案(7)|浏览(124)

我试图通过命令行传递多个文件路径参数到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
有什么想法吗?
谢谢

deyfvvtc

deyfvvtc1#

您描述命令行选项的方式与大多数人期望使用它们的方式不同。通常,命令行选项将接受单个参数,没有前选项的参数将作为参数传递。如果一个参数需要多个元素(比如一个文件列表),我建议使用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,"[,:]")
wkyowqbh

wkyowqbh2#

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

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

qzlgjiam3#

在脚本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"
e0bqpujr

e0bqpujr4#

在四处搜索并避免自下而上编写一个新包之后,我发现使用包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
欢呼

igetnqfo

igetnqfo5#

如果输入参数是相同长度的列表,@agstudy的解决方案无法正常工作。默认情况下,sapply会将相同长度的输入折叠成一个矩阵,而不是一个列表。修复方法很简单,只需在sapply解析参数时显式地将simplify设置为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)
nukf8bse

nukf8bse6#

我也遇到了同样的问题,我开发的解决方法是在输入命令行参数被馈送到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

>

这种方法的一个小限制是,如果任何其他参数有可能接受一个“管道”字符作为有效输入,那么这些参数将不会被正确处理。但是,我认为您可能也可以开发一个稍微复杂一些的解决方案来正确处理这种情况。这个简单的版本大部分时间都有效,并说明了总体思路。

ss2ws0br

ss2ws0br7#

只是遇到了这个问题,幸运的是,{argparser}包支持多值参数。add_argument()函数中有nargs参数,将其指定为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

相关问题