使用 Apache Commons CLI 开发命令行工具
http://www.ibm.com/developerworks/cn/java/j-lo-commonscli/index.html
使用 Apache Commons CLI 開發(fā)命令行工具
楊 暉, 資深軟件工程師, IBM 楊暉,目前就職于 IBM 中國系統(tǒng)與科技研發(fā)中心(CSTL),是一名資深軟件工程師,他目前主要從事 IBM System Director 和存儲配置管理軟件(Storage Configuration Manager)的開發(fā)。另外,他對 Common Information Model (CIM)和其他開源項目也比較感興趣。 羅 文剛, 軟件工程師, IBM 羅文剛,目前就職于 IBM 中國系統(tǒng)與科技研發(fā)中心(CSTL),是一名軟件工程師,他目前主要從事 IBM System Director 的開發(fā)。簡介:?雖然各種人機交互技術(shù)飛速發(fā)展,但最傳統(tǒng)的命令行模式依然被廣泛應用于各個領(lǐng)域:從編譯代碼到系統(tǒng)管理,命令行因其簡潔高效而備受寵愛。各種工具和系統(tǒng)都提供了詳盡的使用手冊,有些還提供示例說明如何二次開發(fā)。然而關(guān)于如何開發(fā)一個易用、強壯的命令行工具的文章卻很少。本文將結(jié)合 Apache Commons CLI,通過一個完整的例子展示如何準備、開發(fā)、測試一個命令行工具。希望本文對有相關(guān)需求的讀者能有所幫助。
平均分 (11個評分)為本文評分
基于 Apache Commons CLI 的命令行設(shè)計
通常情況下命令行處理有三個步驟:定義,解析和詢問階段。本章節(jié)將依次解釋這三個步驟,并將結(jié)合實例來討論如何通過 Apache Commons CLI 來實現(xiàn)它們。由于本文作者一直從事和存儲相關(guān)的工作,所以我們將設(shè)計一個如何向 Server 中添加 / 刪除存儲數(shù)據(jù)源的 CLI。
以下是具體應用場景,用戶可以通過以下方式來添加/刪除通過 CIM (Common Information Model) Server 管理的存儲數(shù)據(jù)源:
| 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 |
| 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 定義階段
每一條命令行都必須定義一組參數(shù),它們被用來定義應用程序的接口。Apache Commons CLI 使用 Options 這個類來定義和設(shè)置參數(shù),它是所有 Option 實例的容器。在 CLI 中,目前有兩種方式來創(chuàng)建 Options,一種是通過構(gòu)造函數(shù),這是最普通也是最為大家所熟知的一種方式;另外一種方法是通過 Options 中定義的工廠方式來實現(xiàn)。
CLI 定義階段的目標結(jié)果就是創(chuàng)建 Options 實例。
根據(jù)上面給出的具體命令行設(shè)計,我們可以用如下代碼片段為添加數(shù)據(jù)源操作定義 Options:
清單 1. 定義 Options 代碼片段
| // 創(chuàng)建 Options 對象Options options = new Options(); // 添加 -h 參數(shù)options.addOption("h", false, "Lists short help"); // 添加 -t 參數(shù)options.addOption("t", true, "Sets the HTTP communication protocol for CIM connection"); |
其中 addOption() 方法有三個參數(shù),第一個參數(shù)設(shè)定這個 option 的單字符名字,第二個參數(shù)指明這個 option 是否需要輸入數(shù)值,第三個參數(shù)是對這個 option 的簡要描述。在這個代碼片段中,第一個參數(shù)只是列出幫助文件,不需要用戶輸入任何值,而第二個參數(shù)則是需要用戶輸入 HTTP 的通信協(xié)議,所以這兩個 option 的第二個參數(shù)分別為 false 和 true,完整的代碼及注釋請參考第二章節(jié)的命令行開發(fā)部分。
CLI 解析階段
在解析階段中,通過命令行傳入應用程序的文本來進行處理。處理過程將根據(jù)在解析器的實現(xiàn)過程中定義的規(guī)則來進行。在 CommandLineParser 類中定義的 parse 方法將用 CLI 定義階段中產(chǎn)生的 Options 實例和一組字符串作為輸入,并返回解析后生成的 CommandLine。
CLI 解析階段的目標結(jié)果就是創(chuàng)建 CommandLine 實例。
根據(jù)上面給出的具體命令行設(shè)計,我們可以用如下代碼片段為添加數(shù)據(jù)源操作解析 Options:
清單 2. 解析 Options 代碼片段
| CommandLineParser parser = new PosixParser(); CommandLine cmd = parser.parse(options, args); if(cmd.hasOption("h")) { // 這里顯示簡短的幫助信息} |
我們需要判斷命令行中是不是有 h 參數(shù),如果有,就需要應用程序列出簡短的幫助信息,完整的代碼及注釋請參考第二章節(jié)的命令行開發(fā)部分。
CLI 詢問階段
在詢問階段中,應用程序通過查詢 CommandLine,并通過其中的布爾參數(shù)和提供給應用程序的參數(shù)值來決定需要執(zhí)行哪些程序分支。這個階段在用戶的代碼中實現(xiàn),CommandLine 中的訪問方法為用戶代碼提供了 CLI 的詢問能力。
CLI 詢問階段的目標結(jié)果就是將所有通過命令行以及處理參數(shù)過程中得到的文本信息傳遞給用戶的代碼。
根據(jù)上面給出的具體命令行設(shè)計,我們可以用如下代碼片段為添加數(shù)據(jù)源操作詢問 Options:
清單 3. 詢問 Options 代碼片段
| // 獲取 -t 參數(shù)值String protocol = cmd.getOptionValue("t"); if(protocol == null) { // 設(shè)置默認的 HTTP 傳輸協(xié)議} else { // 設(shè)置用戶自定義的 HTTP 傳輸協(xié)議 } |
如果用戶設(shè)置了 t 參數(shù),getOptionValue() 方法將獲取用戶設(shè)定的數(shù)值,如果沒有指定該參數(shù),getOptionValue() 方法將返回 null,應用程序會根據(jù)返回的數(shù)值來決定代碼的運行,完整的代碼及注釋請參考第二章節(jié)的命令行開發(fā)部分。
回頁首
基于 Apache Commons CLI 的命令行開發(fā)
Apache Commons CLI 簡介
Apache Commons CLI 是 Apache 下面的一個解析命令行輸入的工具包,該工具包還提供了自動生成輸出幫助文檔的功能。
Apache Commons CLI 支持多種輸入?yún)?shù)格式,主要支持的格式有以下幾種:
CLI 命令代碼實現(xiàn)
命令行程序處理流程相對比較簡單,主要流程為設(shè)定命令行參數(shù) -> 解析輸入?yún)?shù) -> 使用輸入的數(shù)據(jù)進行邏輯處理。下圖是以 rmdatasource 為例的運行流程圖,圖中加入了實際實現(xiàn)過程中的部分邏輯。
圖 1. 命令行運行流程圖
清單 5 是 rmdatasource 的實現(xiàn)代碼片段,主要邏輯在 simpleTest 函數(shù)里面。在里面我們首先載入了 ResourceBundle 用于程序的多語言支持,將運行中的輸出信息預先定義在配置文件中(如 xml 或 property 文件),然后用 ResourceBundle 讀取,這樣在運行時可以根據(jù)運行環(huán)境的 locale 來決定輸出信息語言,也可以讓用戶指定輸出信息的語言。
然后程序使用 Options 定義命令行的參數(shù),由于參數(shù)形式比較簡單,所以使用了 Options 的 addOption 來創(chuàng)建一個參數(shù)設(shè)定(即 option),如果有復雜參數(shù)形式,可以使用 OptionBuilder 來生成 Option,示例如下:
清單 4. 使用 OptionBuilder 生成 Options 代碼片段
| Option help = new Option("h", "the command help"); Option user = OptionBuilder.withArgName("type").hasArg().withDescription( "target the search type").create("t"); // 此處定義參數(shù)類似于 java 命令中的 -D<name>=<value> Option property = OptionBuilder.withArgName("property=value") .hasArgs(2).withValueSeparator().withDescription( "search the objects which have the target property and value").create("D"); Options opts = new Options(); opts.addOption(help); opts.addOption(user); opts.addOption(property); |
在參數(shù)設(shè)定好后,程序使用 BasicParser 類來解析用戶輸入的參數(shù),當參數(shù)中包含 –h 時程序打印命令行的幫助信息(利用 Apache Commons CLI 的 HelpFormatter 自動生成),如果不包含 –h 時,程序?qū)⒆x取需要的 ip、port 等參數(shù)并進行校驗,校驗通過后則調(diào)用內(nèi)部接口進行 rmdatasource 的操作。
清單 5. rmdatasource 代碼片段
| public class RMDataSource { /** * @param args 輸入?yún)?shù)*/ public static void main(String[] args) { simpleTest(args); } public static void simpleTest(String[] args) { ResourceBundle resourceBundle = ResourceBundle.getBundle("message", Locale.getDefault()); Options opts = new Options(); opts.addOption("h", false, resourceBundle.getString("HELP_DESCRIPTION"));opts.addOption("i", true, resourceBundle.getString("HELP_IPADDRESS")); opts.addOption("p", true, resourceBundle.getString("HELP_PORT")); opts.addOption("t", true, resourceBundle.getString("HELP_PROTOCOL")); BasicParser parser = new BasicParser(); CommandLine cl; try { cl = parser.parse(opts, args); if (cl.getOptions().length > 0) { if (cl.hasOption('h')) { HelpFormatter hf = new HelpFormatter(); hf.printHelp("Options", opts); } else { String ip = cl.getOptionValue("i"); String port = cl.getOptionValue("p"); String protocol = cl.getOptionValue("t"); if(!CIMServiceFactory.getinstance().isIPValid(ip)){ System.err.println(resourceBundle.getString("INVALID_IP"));System.exit(1); } try { int rc = CIMServiceFactory.getinstance().rmdatasource( ip, port, protocol); if (rc == 0) { System.out.println(resourceBundle .getString("RMDATASOURCE_SUCCEEDED")); } else { System.err.println(resourceBundle .getString("RMDATASOURCE_FAILED")); } } catch (Exception e) { System.err.println(resourceBundle .getString("RMDATASOURCE_FAILED")); e.printStackTrace(); } } } else { System.err.println(resourceBundle.getString("ERROR_NOARGS")); } } catch (ParseException e) { e.printStackTrace(); } } } |
回頁首
基于 Apache Commons CLI 的命令行測試
CLI 的測試可以分為兩種情況,內(nèi)部邏輯的測試可以用 JUnit 來實現(xiàn),方便、簡潔、快速;CLI 的運行可以用腳本(在 Windows 里用 bat 腳本,Linux 上用 shell 腳本)來驗證 CLI 的運行結(jié)果。
JUnit 的運行與測試
在代碼清單 5 中,我們可以看到 CLI 接收的參數(shù)來源于它的一個 String 輸入數(shù)組,因此我們可以在 JUnit 的 TestCase 中創(chuàng)建一個 String 數(shù)組來模擬輸入,以下清單 6 是對 rmdatsource 的 help 有無參數(shù)情況的單元測試的代碼,僅測試了方法的返回值。
清單 6. JUnit 測試代碼片段
| // 測試帶有 –h 參數(shù)的代碼功能public void testHelp() { String args[]={"-h"}; assertEquals(0, RMDataSource.simpleTest(args)); } // 測試沒有帶 –h 參數(shù)的代碼功能public void testNoArgs() { String args[] = new String[0]; assertEquals(1, RMDataSource.simpleTest(args)); } // 測試輸入所有正確參數(shù)的代碼功能public void testRMDataSource() { /** * 此字符串參數(shù)等同于在命令行窗口輸入命令 java rmdatasource -i 192.168.0.2 -p 5988 -t http */ String args[] = new String[]{"-i","192.168.0.2","-p","5988","-t","http"}; assertEquals(0, RMDataSource.simpleTest(args)); } |
CLI 的運行與測試
開發(fā)完成后,將項目編譯成 jar 包即可運行 CLI 了,假定我們的 jar 包名字是 rmdatasource.jar, 則在 jar 包所在目錄運行 java –jar rmdatasource.jar –h 即可得到該 CLI 的幫助信息。同樣,也可以用腳本來運行所開發(fā)的命令行工具,對命令的返回值和輸出進行校驗,如下圖清單 7 即為一段 shell 腳本獲取命令的返回值和輸出信息的代碼。
清單 7. CLI 測試代碼片段
| /opt/ibm/java-i386-60/bin/java -jar /tmp/test/rmdatasource.jar -h > /tmp/test/result echo "cli return code is $?"if [ $? -eq 0 ] then while read line do echo $line done < /tmp/test/result fi |
回頁首
總結(jié)
隨著科學計算可視化及多媒體技術(shù)的飛速發(fā)展,人機交互技術(shù)不斷更新,但是最傳統(tǒng)的命令行模式依然被廣泛的應用于多個領(lǐng)域,因為命令行界面要較圖形用戶界面節(jié)約更多的計算機系統(tǒng)資源。在熟記命令的前提下,使用命令行界面往往要較使用圖形用戶界面的操作速度要快。同時,命令行模式也更加有利于客戶進行二次開發(fā),方便應用程序的整合。Apache Commons CLI 提供了很多實用的工具和類實現(xiàn),進一步方便了我們對命令行工具的開發(fā),本文介紹了一個完整的實例,希望能對相關(guān)讀者在以后的工作有所幫助。
參考資料
學習
- Apache Commons CLI 官方網(wǎng)站:詳細介紹了 Apache Commons CLI 的概念、各階段的定義與實現(xiàn)以及各個實用類的 JavaDoc。
- Apache Commons CLI 用法簡介:本技術(shù)文檔列舉了一些 Apache Commons CLI 中常用的 API,并給出了一些 API 的用例。
- JUnit Cookbook:本文檔簡略描述了如何使用 JUnit 來編寫測試實例。
- Java Internationalization: Localization with ResourceBundles:本技術(shù)文檔描述了如何使用 ResourceBundle 來實現(xiàn)程序的本土化支持。
- developerWorks Java 技術(shù)專區(qū):這里有數(shù)百篇關(guān)于 Java 編程各個方面的文章。
討論
- 加入 developerWorks 中文社區(qū)。查看開發(fā)人員推動的博客、論壇、組和維基,并與其他 developerWorks 用戶交流。
?
總結(jié)
以上是生活随笔為你收集整理的使用 Apache Commons CLI 开发命令行工具的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 使用javassist动态注入代码
- 下一篇: Nexus配置内部仓库