Bootstrap

【Ruby】Ruby实现命令行工具——OptionParser

在阅读的过程中有任何问题,欢迎一起交流

邮箱:[email protected]   

QQ:1494713801

 

 

    除了 Rails 之外,Ruby 能做的太多太多了,除了用于 Rails 开发之外,Ruby 用的最多的就是写各种 Command Line 工具来解决各种小问题,Command Line 工具又称为命令行工具。

用 Ruby 写命令行工具

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

用OptionParser创建命令行工具

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

+--------------+
| OptionParser |<>-----+
+--------------+       |                      +--------+
                       |                    ,-| Switch |
     on_head -------->+---------------+    /  +--------+
     accept/reject -->| List          |<|>-
                      |               |<|>-  +----------+
     on ------------->+---------------+    `-| argument |
                        :           :        |  class   |
                      +---------------+      |==========|
     on_tail -------->|               |      |pattern   |
                      +---------------+      |----------|
OptionParser.accept ->| DefaultList   |      |converter |
             reject   |(shared between|      +----------+
                      | all instances)|
                      +---------------+

    通常的Unix命令行参数包含下面这些形式:

  • Option - Option主要功能是用于调整命令行工具的行为,Option的表现通常有两种形式,short option或者long option。Option的类型有两种,switchflagswitch不带argument,而flag带有argument。
  • Argument - Argument通常表示命令行工具要操作的对象,通常是路径、URL或者名称等等。
  • Action - 表示命令行工具的行为,比如git命令的push或者pull等等。

    举个例子git log --max-count=10git是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 是怎么用的:

#!/usr/bin/env ruby

require 'optparse'

options = {}
option_parser = OptionParser.new do |opts|
  # 这里是这个命令行工具的帮助信息
  opts.banner = 'here is help messages of the command line tool.'

  # Option 作为switch,不带argument,用于将 switch 设置成 true 或 false
  options[:switch] = false
  # 下面第一项是 Short option(没有可以直接在引号间留空),第二项是 Long option,第三项是对 Option 的描述
  opts.on('-s', '--switch', 'Set options as switch') do
    # 这个部分就是使用这个Option后执行的代码
    options[:switch] = true
  end

  # Option 作为 flag,带argument,用于将argument作为数值解析,比如"name"信息
  #下面的“value”就是用户使用时输入的argument
  opts.on('-n NAME', '--name Name', 'Pass-in single name') do |value|
    options[:name] = value
  end

  # Option 作为 flag,带一组用逗号分割的arguments,用于将arguments作为数组解析
  opts.on('-a A,B', '--array A,B', Array, 'List of arguments') do |value|
    options[:array] = value
  end
end.parse!

puts options.inspect

执行结果

$ ruby my_awesome_command.rb -h
here is help messages of the command line tool.
    -s, --switch                     Set options as switch
    -n, --name Name                  Pass-in single name
    -a, --array A,B                  List of arguments

$ ruby my_awesome_command.rb -s
{:switch=>true}

$ ruby my_awesome_command.rb -n Daniel
{:switch=>false, :name=>"Daniel"}

$ ruby my_awesome_command.rb -a Foo,Bar
{:switch=>false, :array=>["Foo", "Bar"]}

补充2点说明

  • 参数 opts.on('-s'和 opts.on('-s name'的区别是前者不用传参数
  • opts只是用于解析参数的,而不是执行,执行代码应在end.parse!之后,把options作为参数传入

希望以上内容能够帮助你掌握写出符合Unix标准的命令行参数的工具,如果要写出易用,对用户友好,跟其他命令行工具互动良好,可测试,可维护,可格式化输出内容的真正awesome的命令行工具,您仍然需要继续努力,加油吧!



;