R语言接受命令行参数的三种方式

R语言接受命令行参数的三种方式

最近要用一下R的命令行,但是发现命令行参数不是很友好,就总结了一下目前常用的R的命令行参数的接受方法。

平常我们使用R语言的时候常常都是在交互界面中一步一步更改运行,所以一般用不到命令来执行,但有时候对于批量运行或者某种情况下数据较大,直接在Rstudio中运行卡顿,在把代码提前调试好后后续使用命令行可能更好,另外一半我们编写好的脚本搭建pipeline的时候需要命令行执行,这个时候就必须要命令行来实现了。

R接受命令的参数有三个常见的方法commandArgs()getopt()OptionParser(),其中第一个是R自带的函数,后面两个分别来自包getoptoptparse

运行命令行脚本

在之前了解一下R语言的命令行方式,下面在windows的cmd中还是Mac或者Linux的终端中的命令行用法都是这样的。[options]是R脚本解释器的参数,[args]是脚本的参数。如果是Linux或者Mac的话可以省略Rscipt,在脚本第一行加上shebang#!/usr/bin/Rscript

Rscript [options] file [args]

下面是一个R脚本示例test.R

print("Hello R!")

在终端中运行

Rscript test.R

成功输出

[1] "Hello R!"

1. commandArgs()

这是个R内置命令,和perl@ARGV或者和pythonsys.argv类似,就是将来自于命令的参数存入向量(数组)中。但是与perlpython的不同,它的前面几个元素不是命令行的参数,先尝试打印一下这个参数是怎样的。

下面是一个测试脚本test.R

# commandArgs()就返回一个包含参数信息的向量
args <- commandArgs()
print(args)

开始测试

Rscript test_args.R Hello R

输出为

[1] "C:\\PROGRA~1\\R\\R-35~1.2\\bin\\x64\\Rterm.exe"
[2] "--slave"
[3] "--no-restore"
[4] "--file=test_args.R"
[5] "--args"
[6] "Hello"
[7] "R"

参数解释:

位置 说明 示例
1 R所在的路径 C:\PROGRA1\R\R-351.2\bin\x64\Rterm.exe
2 Rscript的参数 --slave
3 Rscript的参数 --no-restore
4 运行R脚本的路径 --file=test_args.R
5 脚本参数flag --args
6 第一个参数 Hello
7 第二个参数 R

那么就是说输入的参数是从第6个开始(注意!R的索引与python或者perl不同,它是从1开始的。)

其实对于参数的位置是可变的,在R所在路径R脚本的路径这两个参数之间是Rscript的参数,这些参数的数量是可变的(其实这里也就是是按照顺序的Rscript的参数,后面的脚本其实也就是一个参数而已,这里看来和python或者perl是差不多的。)

2和3这两个参数虽然我们没有输入,但是是作为Rscript的默认参数的。

参数索引  1       2       3            4                  5      6     7
         Rscript --slave --no-restore --file=test_args.R --args Hello R

这样一来就会导致R脚本的参数的索引不固定,针对这种情况,你也可以添加一个参数来削掉R脚本参数之前的参数了。

args <- commandArgs(trailingOnly = TRUE)
print(args)

输出为

[1] "Hello"
[2] "R"

这样R脚本的参数就从1开始了。

但是这样对于参数解析的顺序是需要的,也就是说脚本接受的参数的输入需要严格按照顺序来,有时候为了区分参数使用这个命令其实是不方便的。使用特定的flag来区分参数是需要,当然R也是有相应的包来做这些事情的。

2. getopt

在使用之前需要安装对应的包:

install.packages("getopt")

这个包的getopt()函数使用方法是

getopt(spec = NULL, opt = commandArgs(TRUE),command = get_Rscript_filename(), usage = FALSE,debug = FALSE)
  • spec:这个参数需要一个矩阵,这个矩阵描述了接受参数的flag以及参数的形式等等信息
第一列 第二列 第三列 第四列 第五列
说明 参数的长名称(多个字符) 参数的短名称(一个字符) 这个flag对应的参数形式(0表示flag不接受参数;1表示可接可不接;2表示必须接参数) 参数的类型(logical;integer;double;complex;character;numeric) 注释信息
示例 "first_arg" "f" 2 "double" "This arg is the first arguments"
  • opt:表示参数的来源,一般都是使用默认的,也就是commandArgs()接受

  • usage:用法,就是说明帮助信息,默认为FALSE

下面是一个示例,将下列代码存到test.R文件中:

library(getopt)

# 首先将第一个参数的具体信息进行描述
# 每行五个,第五个可选,也就是说第五列可以不写
# byrow 按行填充矩阵的元素
# ncol  每行填充五个元素
spec <- matrix(
  c("first",  "f", 2, "integer", "This is first!",
    "second", "s", 1, "character",  "This is second!",
    "third",  "t", 2, "double",  "This is third!",
    "help",   "h", 0, "logical",  "This is Help!"),
  byrow=TRUE, ncol=5)

# 使用getopt方法
opt <- getopt(spec=spec)

# opt实际上就是一个列表,直接使用$来索引到对应的参数的值
print(opt$first)
print(opt$second)
print(opt$third)

在命令行执行

Rscript test.R -f 123 -t 1.1 -s Hello

输出为:

[1] 123
[1] "Hello"
[1] 1.1

但是这里如果参数输出不正确的时候并不能弹出详细的帮助信息,下面增加一些信息就可以得到相应的帮助信息了

library(getopt)

# 首先将第一个参数的具体信息进行描述
spec <- matrix(
# 每行五个,第五个可选,也就是说第五列可以不写
# byrow 按行填充矩阵的元素
# ncol  每行填充五个元素
  c("first",  "f", 2, "integer", "This is first!",
    "second", "s", 1, "character",  "This is second!",
    "third",  "t", 2, "double",  "This is third!",
    "help",   "h", 0, "logical",  "This is Help!"),
  byrow=TRUE, ncol=5)

# 使用getopt方法,注意这里的usage默认是关闭的,这里不能打开
opt <- getopt(spec=spec)

# 这个时候需要将usage参数打开,这样getpot()就会返回一个特定写法的帮助文件
# 当然你也可以自己写帮助,然后将if判断语句中的打印的信息换成你自己定义的帮助信息
if( !is.null(opt$help) || is.null(opt$first) || is.null(opt$third) ){
    # ... 这里你也可以自定义一些东放在里面
    cat(paste(getopt(spec=spec, usage = T), "\n"))
    quit()
}

# opt实际上就是一个列表,直接使用$来索引到对应的参数的值
print(opt$first)
print(opt$second)
print(opt$third)

命令行执行

Rscript test.R -h

输出为:

Usage: test_args.R [-[-first|f] [<integer>]] [-[-second|s] <character>] [-[-third|t] [<double>]] [-[-help|h]]
    -f|--first     This is first!
    -s|--second    This is second!
    -t|--third     This is third!
    -h|--help      This is Help!

解析参数的包不止一个,R也不例外,这里也有另外一个包也是干同样的事情的,但是它的用法与上面的getopt包有一定差别。

3. optparse

使用之前安装包

install.packages("optparse")

其中的方法OptionParser()的用法为:

OptionParser(usage = "usage: %prog [options]", option_list = list(),
  add_help_option = TRUE, prog = NULL, description = "",
  epilogue = "")

其中最重要的参数是option_list,这个参数接受一个列表,这个列表是被用来描述命令参数的解析方式的。

下面是一个例子,将下列代码存到test.R文件中:

library(optparse)

# 描述参数的解析方式
option_list <- list(
  make_option(c("-f", "--first"), type = "integer", default = FALSE,
              action = "store", help = "This is first!"
  ),
  make_option(c("-s", "--second"), type = "character", default = FALSE,
              action = "store", help = "This is second!"
  ),
  make_option(c("-t", "--third"), type = "double", default = FALSE,
              action = "store", help = "This is third!"
  ),
  # make_option(c("-h", "--help"), type = "logical", default = FALSE,
  #             action = "store_TRUE", help = "This is Help!"
  # )
)

# 解析参数
opt = parse_args(OptionParser(option_list = option_list, usage = "This Script is a test for arguments!"))

print(opt)

注意,这个模块不用加上-h的flag,不然会报错:

Error in parse_args(OptionParser(option_list = option_list)) :
  Error in getopt(spec = spec, opt = args) :
  redundant long names for flags (column 1 of spec matrix).

其实从这个报错信息可以看出来这个模块其实还是调用了getopt模块

直接运行

Rscript test.R -f 10 -s Hello -t 1.1

结果:

$first      
[1] 10      
            
$second     
[1] "Hello" 
            
$third      
[1] 1.1     
            
$help       
[1] FALSE

如果加上-h

Usage: This Script is a test for arguments!     
                                                
                                                
Options:                                        
        -f FIRST, --first=FIRST                 
                This is first!                  
                                                
        -s SECOND, --second=SECOND              
                This is second!                 
                                                
        -t THIRD, --third=THIRD                 
                This is third!                  
                                                
        -h, --help                              
                Show this help message and exit 

这里Usage后面跟的信息时候在OptionParser中usage方法指定。

试了试这三个方式,第一个是最简洁的,如果你的脚本输出入参数很少(只有一两个的话),那就采用commandArgs()方法吧,使用轻便;其他两个模块干的活很相似,但是我感觉第三个optparse在接受参数的描述上与python的optparse模块的风格更加像一些,好像模块名也是一样的,里面的参数都很像,所以用过python的会对这个模块很熟悉,另外就是自动生成的帮助文档相对于getopt的来说更加友好一些,生成的解析命令行的描述不容易出错。但是就风格上,getopt更加轻便,解析命令行的参数的描述就是一个矩阵,看起来更加清晰一些。

如需转载,请注明来源://www.greatytc.com/p/8797972113d7

参考

说明,本文部分参考青萍,你好的博客,部分经过整理与修改。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 206,968评论 6 482
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 88,601评论 2 382
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 153,220评论 0 344
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 55,416评论 1 279
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 64,425评论 5 374
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,144评论 1 285
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,432评论 3 401
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,088评论 0 261
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 43,586评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,028评论 2 325
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,137评论 1 334
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,783评论 4 324
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,343评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,333评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,559评论 1 262
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,595评论 2 355
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,901评论 2 345