用OptionParser构建Command Line工具

Ruby除了rails还能做什么?

这两天Ruby-China上有个一个讨论,关于Ruby除了rails还能做什么,其实除了Rails之外,Ruby能做的太多太多了,不过就我来说,除了用于Rails开发之外,Ruby我用的最多的就是写各种Command Line工具来解决各种小问题,Command Line工具又称为命令行工具。

提到用Ruby写命令行工具,就绕不过一个问题,如何解析命令行参数?

Unix下的命令行工具

先啰嗦一下Unix下的命令行工具,Unix的命令行工具历史悠久,这里面故事非常非常多(以后再讲,或者参见Unix编程艺术)。随着时间的推移,对于如何正确构建优良的命令行工具,Unix社区慢慢形成了一整套完整的Convertion以及惯用法,如果你的命令行工具遵从这些Convertion,那么用户将会非常容易的去使用你的命令行工具,甚至通过简洁的方式,将你的命令行工具和各种其他工具组合起来,用来完成各种复杂的操作。

正确的处理命令行参数对于写出高质量的命令行工具非常重要,那么如何正确的处理命令行参数呢?如果有C语言编程经验,或者用C语言写过命令行工具的人可能很熟悉getopt(GNU getopt_long()),getopt是C Library中一个专门用于解析命令行参数的工具,通常用C去写命令行工具的时候,getop是一个很自然选择。

用Ruby写命令行工具

当使用Ruby写命令行工具的时候,我们在不借助任何内置/外置的命令行参数解析工具的情况下,可以直接从ARGV取到传入命令行的参数,然后手工判断,验证并执行后续操作。不过从遵循Unix的命令行工具的Convertion角度来讲,我不建议你直接从ARGV取数值,而是利用现有的库来作这件事情。Ruby的标准库内置提供了一个getopt的Ruby实现GetoptLongGetoptLong基本上模拟了C语言版本的全部借口/功能,不过Ruby开发社区不推荐你使用GetoptLong,而是建议使用另外一个也是内置的且更加强大的解析库:OptionParser

这个世界上总是有人不断的重新发明轮子,除了Ruby已经内置的OptionParser,还有下面这些第三方实现的轮子:

Thor是Rails 3以后内建的命令行工具,严格意义上说,Thor不仅仅用于解析命令行参数,而是用于替代rake作为新的task标准工具,Thor的命令行参数的Parser是自己实现的,我个人建议在写Rails的task的时候,把Thor作为首选,但是作一般用途的命令行工具,Thor有点overkill了。

Gli是一个用于建立“Git-Like Interface Command Line Parser”的工具,这里我简单给出一个什么是“Git-Like”的解释。通常Unix下的命令行工具都符合一个哲学,即“作一件事并且把它做好”,但是有些功能强大复杂的工具,如git,可以通过指定不同的Action执行不同的操作,比如gitpushpull操作:

$ git push
$ git pull

就是两个完全不同的操作,但是他们的command部分都是git,只是action部分不同。我们也可以把这样的通过不同的action来实现不同的操作的命令行工具叫做Command-Suit工具,即从功能上看,它不是一个命令,而是一个命令的suit集合。Gli就是帮助你快速实现这种Command-Suit的框架,如果你需要编写复杂的命令行工具,Gli是一个不错的选择。

TrollopChoiceOptiflag都是命令行参数的Ruby Parser,他们的目的一致,而且他们解析过程都遵循Unix的约定,只是实现各有不同,用法也不同,不过对我来说,他们都是一回事。就Unix命令行来说,参数只有Options,Arguments,以及Actions而已,所以具体用哪个,看你的个人喜好,简单对比下来我认为Choice的DSL语法最易读,简洁,优雅,如果你需要这些第三方Command Line parser的时候,不妨考虑一下Choice。不过我奉行另外一个原则,如果系统内置了的,我就不考虑第三方gem,而且Ruby内置的OptionParser足够强大,能满足我对解析Unix的命令行参数的一切需求,所以我优选使用OptionParser。这里我简单猜测一下为什么还有这么多第三方的轮子,第一是不知道Ruby已经内置了这个,第二个可能就是不爽Ruby内置的这个parser的文档或用法,虽然OptionParser足够强大灵活,但是不代表它好用,容易上手,相反,它的文档就相当坑爹!

用OptionParser创建命令行工具

下面这张图就是Ruby给出的OptionParser的文档,除了这张图片之外就是一个官方范例,然后就没了… 说实话我第一眼看了这张图和官方范例后感觉看不懂,需要反复通过Google各种文章和范例,才了解到了OptionParser的基本用法。 ``` +