使用 Apache Commons CLI 开发命令行工具

虽然各种人机交互技术飞速发展,但最传统的命令行模式依然被广泛应用于各个领域:从编译代码到系统管理,命令行因其简洁高效而备受宠爱。各种工具和系统都提供了详尽的使用手册,有些还提供示例说明如何二次开发。然而关于如何开发一个易用、强壮的命令行工具的文章却很少。本文将结合 Apache Commons CLI,通过一个完整的例子展示如何准备、开发、测试一个命令行工具。希望本文对有相关需求的读者能有所帮助。

基于 Apache Commons CLI 的命令行设计

通常情况下命令行处理有三个步骤:定义,解析和询问阶段。本章节将依次解释这三个步骤,并将结合实例来讨论如何通过 Apache Commons CLI 来实现它们。由于本文作者一直从事和存储相关的工作,所以我们将设计一个如何向 Server 中添加 / 删除存储数据源的 CLI。

以下是具体应用场景,用户可以通过以下方式来添加/删除通过 CIM (Common Information Model) Server 管理的存储数据源:

  1. 添加通过 CIM Server 管理的存储,例如添加 IBM DS Series,命令行设计如下:
    mkdatasource [-h | -? | --help] 
     mkdatasource [-t string] [-i string] [-p string] [-u string] [-w string] [-n string]
     -h 
     Lists short help 
     -t 
     Sets the HTTP communication protocol for CIM connection 
     -i 
    	 Sets the IPv4 address of the device 
     -p 
     Sets the HTTP communication port for CIM connection 
     -u 
    	 Specifies the user ID for the data source connection 
     -w 
    	 Specifies the password for the data source connection 
     -n 
    	 Identifies the namespace of the CIM connection 
    
     Examples: 
     Add CIM Storage Subsystem 
     mkdatasource –t http –i 9.3.194.11 –p 5988 –u admin –w admin –n root/lsiarray13
  2. 删除通过 CIM Server 管理的存储,例如删除 IBM DS Series,命令行设计如下:
    rmdatasource [-h | -? | --help] 
     rmdatasource [-t string] [-i string] [-p string] 
    
     -h 
     Lists short help 
     -t 
     Sets the HTTP communication protocol for CIM connection 
     -i 
     Sets the IPv4 address of the device 
     -p 
     Sets the HTTP communication port for CIM connection 
    
     Examples: 
     Remove CIM Storage Subsystem 
     rmdatasource –t http –i 9.3.194.11 –p 5988

CLI 定义阶段

每一条命令行都必须定义一组参数,它们被用来定义应用程序的接口。Apache Commons CLI 使用 Options 这个类来定义和设置参数,它是所有 Option 实例的容器。在 CLI 中,目前有两种方式来创建 Options,一种是通过构造函数,这是最普通也是最为大家所熟知的一种方式;另外一种方法是通过 Options 中定义的工厂方式来实现。

CLI 定义阶段的目标结果就是创建 Options 实例。

根据上面给出的具体命令行设计,我们可以用如下代码片段为添加数据源操作定义 Options:

清单 1. 定义 Options 代码片段
// 创建 Options 对象
 Options options = new Options(); 

 // 添加 -h 参数
 options.addOption("h", false, "Lists short help"); 

 // 添加 -t 参数
 options.addOption("t", true, "Sets the HTTP communication protocol for CIM connection");

其中 addOption() 方法有三个参数,第一个参数设定这个 option 的单字符名字,第二个参数指明这个 option 是否需要输入数值,第三个参数是对这个 option 的简要描述。在这个代码片段中,第一个参数只是列出帮助文件,不需要用户输入任何值,而第二个参数则是需要用户输入 HTTP 的通信协议,所以这两个 option 的第二个参数分别为 false 和 true,完整的代码及注释请参考第二章节的命令行开发部分。

CLI 解析阶段

在解析阶段中,通过命令行传入应用程序的文本来进行处理。处理过程将根据在解析器的实现过程中定义的规则来进行。在 CommandLineParser 类中定义的 parse 方法将用 CLI 定义阶段中产生的 Options 实例和一组字符串作为输入,并返回解析后生成的 CommandLine。

CLI 解析阶段的目标结果就是创建 CommandLine 实例。

根据上面给出的具体命令行设计,我们可以用如下代码片段为添加数据源操作解析 Options:

清单 2. 解析 Options 代码片段
CommandLineParser parser = new PosixParser(); 
 CommandLine cmd = parser.parse(options, args); 

 if(cmd.hasOption("h")) { 
    // 这里显示简短的帮助信息
 }

我们需要判断命令行中是不是有 h 参数,如果有,就需要应用程序列出简短的帮助信息,完整的代码及注释请参考第二章节的命令行开发部分。

CLI 询问阶段

在询问阶段中,应用程序通过查询 CommandLine,并通过其中的布尔参数和提供给应用程序的参数值来决定需要执行哪些程序分支。这个阶段在用户的代码中实现,CommandLine 中的访问方法为用户代码提供了 CLI 的询问能力。

CLI 询问阶段的目标结果就是将所有通过命令行以及处理参数过程中得到的文本信息传递给用户的代码。

根据上面给出的具体命令行设计,我们可以用如下代码片段为添加数据源操作询问 Options:

清单 3. 询问 Options 代码片段
// 获取 -t 参数值
 String protocol = cmd.getOptionValue("t"); 

 if(protocol == null) { 
    // 设置默认的 HTTP 传输协议
 } else { 
 // 设置用户自定义的 HTTP 传输协议 
 }

如果用户设置了 t 参数,getOptionValue() 方法将获取用户设定的数值,如果没有指定该参数,getOptionValue() 方法将返回 null,应用程序会根据返回的数值来决定代码的运行,完整的代码及注释请参考第二章节的命令行开发部分。