使用 CommandLineApplication 類創建專業的控制臺程序

分類:IT技術 時間:2016-10-07

閑話

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

然而世事就是這樣,錦繡繁華之後就開始返璞歸真,大魚大肉太多就向往點粗茶淡飯,開車開久了就懷念起自行車,GUI 充斥的 Windows 的世界裏似乎也開始掛起一陣控制臺的清風。畢竟,一旦你熟悉了各種命令和參數,敲鍵盤的速度還是勝過鼠標的,只是現在的人都太懶或者太忙,總是寧願犧牲效率而不願意去多記一點東西。

我個人還是很喜歡命令行的,尤其是遠程訪問一個系統的時候,一個簡單的 ssh 命令直接登錄到遠程 linux,一個簡單的 scp 命令就可以互相傳輸文件,這種便利、快捷是 Windows 遠程桌面所無法比擬的。GUI 也許是 Windows 的設計哲學, 做什麽事情都要靠 GUI。 沒錯,這大大降低了各種操作門檻,但是作為一個程序員,GUI 工具並非總是最佳選擇,但除了 GUI 工具,替代選擇並不多——直到 .NET Core 的出現。

專業的控制臺程序

首先我們要有個標準,怎樣才算“專業的控制臺程序”?

平常無論是寫著玩還是工作需要,我都做過一些控制臺程序,在啟動參數的傳入、解析和執行上都比較隨意,類似 MyProgram abc 123 這樣,MyProgram 是程序名,abc123 是參數值,內部直接用 args[0]args[1]取得參數值並使用。僅此而已。時間長了,自己都搞不懂每個參數什麽意思,參數有哪些有效值,都得查源代碼才知道,每個參數的順序也很重要,顛倒不得。而內部實現上,至少是 Main 方法裏是典型的面條式代碼。由此可見,我的這些控制臺程序,無論是外在,還是內在,都業余得很。

那一個專業的控制臺程序應該是什麽樣的呢?

完善的幫助信息

當你面對一個陌生的命令行程序,或者重新面對你自己2個月之前寫的命令行程序,你心裏第一反應會是什麽呢?讓我猜猜,你的第一反應一定是“這貨到底怎麽用?”(不要告訴我我猜錯了,我不相信??),下一個想法就是這個程序能告訴我用法就好了。一個專業的控制臺程序必然會滿足你的這個需要——它可以提供完善的幫助信息。比如 git:

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 裏。

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


Tags: Windows 返璞歸真 蘋果電腦 自行車 控制臺

文章來源:


ads
ads

相關文章
ads

相關文章

ad