将命令行参数传递给R CMD BATCH

121

我一直在使用终端执行命令R CMD BATCH my_script.R来运行一个R脚本。现在我想传递一个参数到该命令,但无法使其正常工作。如果我输入R CMD BATCH my_script.R blabla,那么blabla将成为输出文件,而不是被解释为可供执行的R脚本使用的参数。

我试过了Rscript my_script.R blabla,看起来它可以正确地传递blabla作为一个参数,但是接着我就没有像使用R CMD BATCH时得到的.Rout文件(我想要这个文件)。虽然我可以将调用Rscript的输出重定向到我选择的文件名中,但我将无法在.Rout文件中以R CMD BATCH的方式包括文件中的R输入命令。

因此,理想情况下,我希望能够通过R CMD BATCH方法传递参数给正在执行的R脚本,但如果有办法让Rscript产生相似的.Rout文件,那么我也会很高兴采用这种方法。

6个回答

133

我的印象是R CMD BATCH有点过时了。无论如何,最新的可在所有平台上使用的Rscript可执行文件以及commandArgs()使得处理命令行参数非常容易。

这里有一个小脚本作为示例——把它命名为"myScript.R":

## myScript.R
args <- commandArgs(trailingOnly = TRUE)
rnorm(n=as.numeric(args[1]), mean=as.numeric(args[2]))

这是从命令行调用的样子

> Rscript myScript.R 5 100
[1]  98.46435 100.04626  99.44937  98.52910 100.78853

编辑:

虽然我不建议这样做,但是您可以使用source()sink()的组合来让Rscript生成一个类似于R CMD BATCH所生成的.Rout文件。其中一种方式是创建一个小型的R脚本——叫做RscriptEcho.R——你可以直接用Rscript调用它。脚本可能看起来像这样:

## RscriptEcho.R
args <- commandArgs(TRUE)
srcFile <- args[1]
outFile <- paste0(make.names(date()), ".Rout")
args <- args[-1]

sink(outFile, split = TRUE)
source(srcFile, echo = TRUE)

要执行您的实际脚本,您需要执行以下操作:

Rscript RscriptEcho.R myScript.R 5 100
[1]  98.46435 100.04626  99.44937  98.52910 100.78853

将用提供的参数执行myScript.R, 并将交错的输入、输出和消息写入一个名为.Rout的唯一文件。

Edit2:
您可以详细运行Rscript并将详细输出放入文件中。

Rscript --verbose myScript.R 5 100 > myScript.Rout

8
我也有这样的印象,R CMD BATCH 已经是过时的工具了。不过我喜欢它之处在于它会生成一个 .Rout 文件,该文件不仅包括脚本的输出结果,还会将产生该输出结果的.R脚本文件中的输入命令/注释与输出交错地记录在同一个文件里。 - Bryce Thomas
5
但我认为您可以比R CMD BATCH更好地使用knitr,例如Rscript -e "knitr::stitch(commandArgs(TRUE)[1])" my_script.R(您可以将stitch替换为stitch_rhtmlstitch_rmd,并且需要从Github安装knitr,因为我刚刚发现了一个在stitch中的错误...) - Yihui Xie
9
补充一下我的意见,从终端进行重定向也很容易。例如:Rscript myfile.R > path/to/mylog.Rout,文件的输出不会打印到屏幕上,而是保存在你的.Rout文件中。 - James Pringle
6
补充@JamesPringle的观点,我通常希望我的输出既能在屏幕上打印(以实时监控),也能保存到文件中(以便日后查看)。我使用命令 Rscript myfile.R | tee mylog.Rout - Heisenberg
1
@Yihui的精彩评论必须这样修复:r Rscript -e“library(knitr); knitr :: stitch(commandArgs(TRUE)[1])”my_script.R - petermeissner
显示剩余2条评论

31

在尝试了这里描述的选项后,我发现在 r-bloggers 上 Forester 的这篇文章是一个干净的选择。

我在这里放置他的代码:

从命令行

$ R CMD BATCH --no-save --no-restore '--args a=1 b=c(2,5,6)' test.R test.out &

Test.R

##First read in the arguments listed at the command line
args=(commandArgs(TRUE))

##args is now a list of character vectors
## First check to see if arguments are passed.
## Then cycle through each element of the list and evaluate the expressions.
if(length(args)==0){
    print("No arguments supplied.")
    ##supply default values
    a = 1
    b = c(1,1,1)
}else{
    for(i in 1:length(args)){
      eval(parse(text=args[[i]]))
    }
}

print(a*2)
print(b*3)

在 test.out 文件中

> print(a*2)
[1] 2
> print(b*3)
[1]  6 15 18

感谢Forester


需要注意的是,如果参数的类型是字符,则无需使用单引号/双引号。例如:R CMD BATCH '--args a=FolderName' test.R test.out & - d2a2d
正如Forester的帖子中所提到的,--args是关键。它也可以与R --no-save --no-restore --args a=1 < test.RR --no-save --no-restore < test.R --args a=1一起使用。 - Dave
有没有办法从命令行传递参数到 --args 中?比如说我们想在命令行中进行一个 for 循环,然后将其发送到 --args 行中。 - user2809432
@user2809432,你解决了这个问题吗?我也遇到了类似的问题。 - SpatialProgramming
@user2809432 我成功解决了!使用 $ R 代替 $ R CMD BATCH即:<code> for z in seq $x $y; do echo 运行任务 $z R --file=code/simulate_urban_rural.R --args $z > output done </code> - SpatialProgramming

13
您需要在my_script.R之前放置参数,并在参数前使用-,例如:
R CMD BATCH -blabla my_script.R

commandArgs() 在这种情况下会接收到字符字符串 -blabla。有关详细信息,请参见帮助文档:

$ R CMD BATCH --help
Usage: R CMD BATCH [options] infile [outfile]

Run R non-interactively with input from infile and place output (stdout
and stderr) to another file.  If not given, the name of the output file
is the one of the input file, with a possible '.R' extension stripped,
and '.Rout' appended.

Options:
  -h, --help        print short help message and exit
  -v, --version     print version info and exit
  --no-timing           do not report the timings
  --            end processing of options

Further arguments starting with a '-' are considered as options as long
as '--' was not encountered, and are passed on to the R process, which
by default is started with '--restore --save --no-readline'.
See also help('BATCH') inside R.

2
我注意到如果我这样做,并在脚本中使用args <- commandArgs(FALSE),然后打印args,我最终会得到所有参数,包括那些不是我的,比如--restore--save等。如果我使用commandArgs(TRUE),我根本就没有参数。有没有一种方法只获取我自己的附加参数?--args看起来很有前途,但我还没有能够让它工作... - Bryce Thomas
你必须从末尾开始计算参数(例如,size-2,size-1,size)- 你的参数始终在末尾。 - ynka

9
在你的R脚本中,名为test.R:
args <- commandArgs(trailingOnly = F)
myargument <- args[length(args)]
myargument <- sub("-","",myargument)
print(myargument)
q(save="no")

从命令行运行:

R CMD BATCH -4 test.R

您的输出文件test.Rout将显示参数4已成功传递给R:

cat test.Rout

> args <- commandArgs(trailingOnly = F)
> myargument <- args[length(args)]
> myargument <- sub("-","",myargument)
> print(myargument)
[1] "4"
> q(save="no")
> proc.time()
user  system elapsed 
0.222   0.022   0.236 

4
我添加一个答案,因为我认为一行解决方案总是好的!在你的myRscript.R文件顶部添加以下行:
eval(parse(text=paste(commandArgs(trailingOnly = TRUE), collapse=";")))

然后使用类似以下方式提交您的脚本:

R CMD BATCH [options] '--args arguments you want to supply' myRscript.R &

例如:

R CMD BATCH --vanilla '--args N=1 l=list(a=2, b="test") name="aname"' myscript.R &

然后:

> ls()
[1] "N"    "l"    "name"

0

这是另一种处理命令行参数的方法,使用R CMD BATCH。我的方法基于此前在这里给出的答案,它允许您在命令行上指定参数,并在您的R脚本中为其中一些或全部参数提供默认值。

这是一个R文件,我将其命名为test.R

defaults <- list(a=1, b=c(1,1,1)) ## default values of any arguments we might pass

## parse each command arg, loading it into global environment
for (arg in commandArgs(TRUE))
  eval(parse(text=arg))

## if any variable named in defaults doesn't exist, then create it
## with value from defaults
for (nm in names(defaults))
  assign(nm, mget(nm, ifnotfound=list(defaults[[nm]]))[[1]])

print(a)
print(b)

在命令行中,如果我输入

R CMD BATCH --no-save --no-restore '--args a=2 b=c(2,5,6)' test.R

然后在 R 中,我们将有 a = 2b = c(2,5,6)。但是我可以省略 b,并添加另一个参数 c

R CMD BATCH --no-save --no-restore '--args a=2 c="hello"' test.R

接下来在 R 语言中,我们将会有 a = 2b = c(1,1,1)(使用默认值),以及 c = "hello"

最后,为了方便起见,我们可以将 R 代码包装在一个函数中,只要我们对环境小心谨慎即可:

## defaults should be either NULL or a named list
parseCommandArgs <- function(defaults=NULL, envir=globalenv()) {
  for (arg in commandArgs(TRUE))
    eval(parse(text=arg), envir=envir)

  for (nm in names(defaults))
    assign(nm, mget(nm, ifnotfound=list(defaults[[nm]]), envir=envir)[[1]], pos=envir)
}

## example usage:
parseCommandArgs(list(a=1, b=c(1,1,1)))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接