几个小伙伴在考虑下面这个各个语言都会遇到的问题:

问题:设计一个命令行参数解析API
一个好的命令行参数解析库一般涉及到这几个常见的方面:
1) 支持方便地生成帮助信息
2) 支持子命令,比如:git包含了push, pull, commit等多种子命令
3) 支持单字符选项、多字符选项、标志选项、参数选项等多种选项和位置参数
4) 支持选项默认值,比如:–port选项若未指定认为5037
5) 支持使用模式,比如:tar命令的-c和-x是互斥选项,属于不同的使用模式
经过一番考察,小伙伴们发现了这个几个有代表性的API设计:
1. getopt():
getopt()是libc的标准函数,很多语言中都能找到它的移植版本。
02 | while ((c = getopt(argc, argv, "ac:d:" )) != -1) { |
03 | int this_option_optind = optind ? optind : 1; |
10 | printf ( "option c with value '%s'" , optarg); |
14 | printf ( "option d with value '%s'" , optarg); |
20 | printf ( "?? getopt returned character code 0%o ??" , c); |
getopt()的核心是一个类似printf的格式字符串的命令行参数描述串,如上面的”ac:d:”定义了”a”,
“c”,”d”3个命令行参数,其中,a是一个标志符不需要参数,”c”和”d”需要跟参数。getopt()功能非常弱,只支持单个字符的标志选项和参
数选项。如果按上面的5点来比对,基本上只能说是勉强支持第3点,其他几项只能靠程序自己来实现了,所以,想直接基于getopt()实现一个像git这
样复杂的命令行参数是不可能的,只有自己来做很多的解析工作。小伙伴们看过getopt()之后一致的评价是:图样图森破。
2. Google gflags
接着,小伙伴们又发现了gflags这个Google出品C++命令行参数解析库。
02 | DEFINE_bool(memory_pool, false , "If use memory pool" ); |
03 | DEFINE_bool(daemon, true , "If started as daemon" ); |
04 | DEFINE_string(module_id, "" , "Server module id" ); |
05 | DEFINE_int32(http_port, 80, "HTTP listen port" ); |
06 | DEFINE_int32(https_port, 443, "HTTPS listen port" ); |
08 | int main( int argc, char ** argv) { |
09 | ::google::ParseCommandLineFlags(&argc, &argv, true ); |
11 | printf ( "Server module id: %s" , FLAGS_module_id.c_str()); |
14 | printf ( "Run as daemon: %d" , FLAGS_daemon); |
16 | if (FLAGS_memory_pool) { |
17 | printf ( "Use memory pool: %d" , FLAGS_daemon); |
小伙伴们看了后不由得感叹“真心好用啊”!的确,gflags简单地通过几个宏就定义了命令行选项,基本上很好的支持了上面提到的1,3,4这几
项,比起getopt()来强多了。对于类似cp这样的小命令,gflags应该是够用了,但要达到git这种级别就显得有些单薄了。
3. Ruby Commander
接下来小伙伴们又发现了Ruby Commander库:
03 | program :name , 'Foo Bar' |
04 | program :version , '1.0.0' |
05 | program :description , 'Stupid command that prints foo or bar.' |
07 | c.syntax = 'foobar bar [options]' |
08 | c.description = 'Display bar with optional prefix and suffix' |
09 | c.option '--prefix STRING' , String , 'Adds a prefix to bar' |
10 | c.option '--suffix STRING' , String , 'Adds a suffix to bar' |
11 | c.action do |args, options| |
12 | options.default :prefix => '(' , :suffix => ')' |
13 | say "#{options.prefix}bar#{options.suffix}" |
18 | $ foobar bar --suffix '}' --prefix '{' |
Commander库利用Ruby酷炫的语法定义了一种描述命令行参数的内部DSL,看起来相当高端大气上档次。除了上面的第5项之外,其他几项都
有很好的支持,可以说Commander库的设计基本达到了git这种级别命令行参数解析的要求。只是,要搞懂Ruby这么炫的语法和这个库的使用方法恐
怕就不如getopt()和gflags容易了。有小伙伴当场表示想要学习Ruby,但是也有小伙伴表示再看看其他库再说。 |