使用 CommandLineApplication 類建立專業的控制檯程序

閒話

在好久好久之前,電腦是命令行/終端/控制檯的天下,那屏幕上的光標在行雲流水般的鍵盤敲擊下歡快地飛躍着,那一行行的字符輸出唰唰唰地滾動着……直到 Windows 95 的出現(那時候我還不知道蘋果電腦和它的操做系統),個人鼠標終於再也不召灰,開始有了用武之地,而後就是 GUI 的天下……git

然而世事就是這樣,錦繡繁華以後就開始返璞歸真,大魚大肉太多就嚮往點粗茶淡飯,開車開久了就懷念起自行車,GUI 充斥的 Windows 的世界裏彷佛也開始掛起一陣控制檯的清風。畢竟,一旦你熟悉了各類命令和參數,敲鍵盤的速度仍是賽過鼠標的,只是如今的人都太懶或者太忙,老是寧願犧牲效率而不肯意去多記一點東西。程序員

我我的仍是很喜歡命令行的,尤爲是遠程訪問一個系統的時候,一個簡單的 ssh 命令直接登陸到遠程 Linux,一個簡單的 scp 命令就能夠互相傳輸文件,這種便利、快捷是 Windows 遠程桌面所沒法比擬的。GUI 也許是 Windows 的設計哲學, 作什麼事情都要靠 GUI。 沒錯,這大大下降了各類操做門檻,可是做爲一個程序員,GUI 工具並不是老是最佳選擇,但除了 GUI 工具,替代選擇並很少——直到 .NET Core 的出現。github

專業的控制檯程序

首先咱們要有個標準,怎樣纔算「專業的控制檯程序」?json

日常不管是寫着玩仍是工做須要,我都作過一些控制檯程序,在啓動參數的傳入、解析和執行上都比較隨意,相似 MyProgram abc 123 這樣,MyProgram 是程序名,abc123 是參數值,內部直接用 args[0]args[1]取得參數值並使用。僅此而已。時間長了,本身都搞不懂每一個參數什麼意思,參數有哪些有效值,都得查源代碼才知道,每一個參數的順序也很重要,顛倒不得。而內部實現上,至少是 Main 方法裏是典型的麪條式代碼。因而可知,個人這些控制檯程序,不管是外在,仍是內在,都業餘得很。數組

那一個專業的控制檯程序應該是什麼樣的呢?app

完善的幫助信息

當你面對一個陌生的命令行程序,或者從新面對你本身2個月以前寫的命令行程序,你內心第一反應會是什麼呢?讓我猜猜,你的第一反應必定是「這貨到底怎麼用?」(不要告訴我我猜錯了,我不相信💢),下一個想法就是這個程序能告訴我用法就行了。一個專業的控制檯程序必然會知足你的這個須要——它能夠提供完善的幫助信息。好比 git:ssh

git

有了這些幫助信息,咱們天然信心倍增,內心有譜多了。工具

我但願我寫的控制檯程序也能作到這點!學習

符合「國際慣例」的調用方式

若是你留意一下 Linux 平臺下的一衆控制檯程序,你會發現他們的參數組織和調用方式十分相似,這種約定俗成的「國際慣例」十分有助於下降熟悉各個控制檯程序的學習成本。測試

咱們仍是以 git 爲例,從上面的截圖能夠看出,它有很是多的參數可用。雖然都是參數,但根據做用不一樣,能夠分爲 command, argument, option 三類。我不是控制檯程序達人,對這3類參數的區別與聯繫還在深刻理解、學習中。目前的理解是(以 git 爲例):

  • command

    一個複雜的控制檯程序能夠提供多個子命令,而 command 就表明這些子命令

    好比 cloneinit

  • argument

    是一個 command 須要的參數。

    好比執行 clone 的時候須要指定一個 repository 的地址,這個地址就是一個 argument

  • option

    調整 command 的行爲。

    好比對於 clone 能夠增長 --verbose 參數使其輸出更詳盡的信息。

    一般以兩個短橫線開頭後跟參數名,好比 --verbose,對於經常使用的 option 還會有簡寫形式,就是一個短橫線後跟簡寫形式的參數名,好比 --verbose 支持簡寫形式 -v,在幫助裏一般以 --verbose, -v 或者 --verbose|-v 的形式說明。

    option 能夠有值也能夠沒有值,有值的時候,其賦值方式不一而足,常見的有用空格的 --branch dev,用等號的(注意等號兩邊沒有空格) --branch=dev,用冒號的(冒號兩邊沒有空格) --branch:dev

對於相對簡單的控制檯程序,可能只有 argument 和 option 而並不包括 command。

看看那些有名的控制檯程序,基本上也都遵循這個套路。在這方面,我不想作個怪胎,因此,我但願我寫的控制檯程序也能遵守這些「國際慣例」!

易於維護的內部實現

在控制檯程序的內部實現上,之前的作法很是簡單粗暴,用一堆 if 或者 switch 配合各類 &&|| 成功地作到了一開始只有「上帝」和我明白,1個月後只有「上帝」明白的效果。隨着程序參數增多、邏輯愈來愈複雜,這麼搞下去,「上帝」依然能夠很瀟灑,我會被搞死的。爲了避免讓我變成禿頭,爲了我能夠有更多的時間玩遊戲,控制檯程序的內部實現必須層次分明、易於維護!

問題來了

瞭解了一個專業的控制檯程序應具有的素質,那麼接下來有一個問題縈繞在我心頭久久不願散去……

HOW

做爲一個小白,要實現一個有詳細說明信息、調用方式符合「國際慣例」、還能優雅地處理各類參數的控制檯程序何其困難?而如此套路化的東西難道沒有一套成型的東西供參考嗎?

人生爲什麼如此艱難

CommandLineApplication

天無絕人之路,一次偶然的邂逅,遇到了它—— CommandLineApplication。若是你用 .NET Core 的話,它能夠在你構建專業控制檯程序的路上助你一臂之力。

它全名是 Microsoft.Extensions.CommandLineUtils.CommandLineApplication,家住 GitHub 省 aspnet 市 Common 區 src 路 Microsoft.Extensions.CommandLineUtils 大院 CommandLine 室.若是你路盲,這裏有個傳送門

實踐是檢驗真理的惟一標準 - MathForKids 程序

讓咱們從頭開始,利用 dotnet cli 和 Visual Studio Code 親自體驗一下它到底有多強大。咱們將建立一個 MathForKids 程序,它能夠根據參數輸出一些加減乘除的算式,讓孩子算算結果。

MathForKids 程序的功能

  • 它只輸出算式,因此咱們不要搞得太複雜,不須要什麼子命令 command。

    想了解帶 command 的用法,能夠參考本文最後附上的 CommandLineApplication 的官方測試代碼 😛

  • 它在執行的時候須要指定輸出的算式是加、減、乘、除仍是這4種運算符的組合,所以咱們能夠設置一個 argument: operator.

    這個 argument 能夠容許同時設置多個值。

  • 它能夠設置生成的數字的最大值和最小值,由於它們只是調整輸出的算式種數字的大小,所以我將其歸爲 options: minValue, maxValue

    minValue 的默認值是0

    maxValue 的默認值是100

  • 它能夠設置生成的算式個數,這也只是調整輸出結果,所以我也將其歸爲 option: count

    count 的默認值是 10

MathForKids 程序的用法

在咱們看代碼以前,先看看這個程序用起來應該是什麼樣的。

注意:在本文發佈之時,.NET Core 處於 RC 2 階段,還不支持編譯爲本地可執行文件。因此目前必須使用 dotnet MathForKids.dll 來運行。圖中紅色下劃線表示輸入的命令

首先,它能夠提供幫助信息:

MathForKids 幫助信息

從幫助信息中咱們能夠看到它支持的全部參數,而且支持參數的全寫和簡寫,好比咱們能夠寫 --minValue 也能夠簡寫爲 -min

當咱們調用它只生成加法和乘法,其它選項默認時:

MathForKids 使用默認 option 調用

注意,咱們一次傳入了多個參數: 加 乘

當咱們設置一些 option 來改變輸出結果時:

MathForKids 傳入多個 options

注意,這裏演示了使用全寫和簡寫添加 option 以及以多種方式賦值(使用冒號、等號和空格)。

以上使用方法看起來是否是有點「專業」的味道了?

Show me the code

爲了使用 CommandLineApplication 類,咱們須要添加對 Microsoft.Extensions.CommandLineUtils 的引用:

{
  "version": "1.0.0-*",
  "buildOptions": {
    "emitEntryPoint": true
  },
  "dependencies": {
    "Microsoft.NETCore.App": {
      "type": "platform",
      "version": "1.0.0-rc2-3002702"
    },
    "Microsoft.Extensions.CommandLineUtils": "1.0.0-rc2-final" //<--- add this dependency
  },
  "frameworks": {
    "netcoreapp1.0": {
      "imports": "dnxcore50"
    }
  }
}

這是 MathForKids 程序的主體部分,說明都在註釋裏:

using System;
using Microsoft.Extensions.CommandLineUtils;

namespace ConsoleApplication
{
    public class Program
    {
        public static void Main(string[] args)
        {
            CommandLineApplication app = new CommandLineApplication();
            app.HelpOption("--help|-h|-?"); // 使其支持顯示幫助信息
            app.VersionOption("--version|-v", "1.0.0"); // 使其支持顯示版本信息。爲了簡化起見,直接返回靜態的 1.0.0

            // 添加 argument,這裏咱們容許傳入這個 argument 的多個值。
            CommandArgument argOperator = app.Argument("operator", "算式類型,有效值:加、減、乘、除,能夠設置多個類型", multipleValues: true);

            // 添加多個 options,注意設置全寫和簡寫的方式,很簡單。這應該是基於約定的解析處理方式。
            CommandOption optMin = app.Option("--minValue -min <value>", "最小值,默認爲0", CommandOptionType.SingleValue);
            CommandOption optMax = app.Option("--maxValue -max <value>", "最大值,默認爲100", CommandOptionType.SingleValue);
            CommandOption optCount = app.Option("--count -c <value>", "生成的算式數量,默認爲10", CommandOptionType.SingleValue);

            // 傳入一個委託方法,當下面的 Execute 執行後會執行咱們的委託方法,完成咱們須要處理的工做。 委託方法須要返回一個 int,反映執行結果,一如經典的控制檯程序須要的那樣。
            app.OnExecute(() =>
            {
                return OnAppExecute(argOperator, optMin, optMax, optCount);
            });

            // 開始執行,把控制檯傳入的參數直接傳遞給 CommandLineApplication。
            app.Execute(args);
        }

        private static int OnAppExecute(CommandArgument argOperator, CommandOption optMin, CommandOption optMax, CommandOption optCount)
        {
            // 此處省略 100 行以如下示例取代
            List<string> operators = argOperator.Values;

            string max;
            if(optMax.HasValue())
                max = optMax.Value();

            return 0;
        }
    }
}

CommandArgumentCommandOption 的使用很是簡單,有 HasValueValue 等方法能夠判斷和取值。

全部代碼放在個人 GitHub 裏。

官方的測試類也是一個很好的參考資源。

相關文章
相關標籤/搜索