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实现GetoptLong,GetoptLong基本上模拟了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执行不同的操作,比如git的push和pull操作:
1
| |
1
| |
就是两个完全不同的操作,但是他们的command部分都是git,只是action部分不同。我们也可以把这样的通过不同的action来实现不同的操作的命令行工具叫做Command-Suit工具,即从功能上看,它不是一个命令,而是一个命令的suit集合。Gli就是帮助你快速实现这种Command-Suit的框架,如果你需要编写复杂的命令行工具,Gli是一个不错的选择。
Trollop,Choice和Optiflag都是命令行参数的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的基本用法。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | |
通常的Unix命令行参数包含下面这些形式:
- Option - Option主要功能是用于调整命令行工具的行为,Option的表现通常有两种形式,
short option或者long option。Option的类型有两种,switch或flag,switch不带argument,而flag带有argument。 - Argument - Argument通常表示命令行工具要操作的对象,通常是路径,URL或者名称等等。
- Action - 表示命令行工具的行为,比如
git命令的push或者pull等等。
举个例子git log --max-count=10:git是command。log是action,表示查看git的提交历史。--max-count就是option,表示最多显示N条commit记录。而最后的=10就是argument,表示option的数值,即查看最后10条历史提交记录。所有的Unix命令行工具都遵循这样的一个约定,这里需要主意一下,Argument前面的=在很多命令行工具中是可以省略的。
用OptionParser创建一个简单的命令行工具,通常我们只需要创建一个OptionParser的实例instance,然后给这个instance传入一个block,在这个block内部我们就可以使用OptionParser提供的方法来解析命令行参数,特别是用on方法来根据定义捕捉各种参数,并将参数解析成可被使用的Ruby数据,如String,Boolean,Array以及Hash等。而on方法最让人困惑的地方就是它异常灵活参数处理,比如on方法的第一个参数,如果是一个-加一个非空格字符,则把这个参数当作switch来处理,例如on('-n'),如果是一个-开头的字符,后面跟着一个空格外加另外一个字符,那么就把这个参数当作flag处理,例如on('-n NAME')。如果on方法的参数超过两个,并且两个都是String,那么则视这两个参数表示一个意思,例如on('-n NAME', '--name NAME')。如此这般的例子还有很多,如果有更高需求的朋友,我建议你还是直接去啃源代码吧。
下面我创建一个名为my_awesome_command.rb的命令行工具,这个工具直接输出我的命令行参数解析的结果,我用中文注释来说明OptionParser视怎么用的:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 | |
执行结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | |
希望以上内容能够帮助你掌握写出能解析符合Unix标准的命令行参数的工具,如果要写出易用,对用户友好,跟其他命令行工具互动良好,可测试,可维护,可格式化输出内容的真正awesome的命令行工具,您仍然需要继续努力,加油吧!





