2016 年 9 月

第 31 卷,第 9 期

本文章是由機器翻譯。

基礎 .NET - .NET Core 1.0 的命令列處理

Mark Michaelis

作者:Mark Michaelis在本月份的基本.NET 專欄中,我將繼續我成各種功能進行的調查,.NET Core 這次的完整發行版本 (不在 beta 版或 euphemistic 發行候選版本)。具體來說,我要把焦點放在其命令列公用程式 (這在.NET Core 通用程式庫中找到 github.com/aspnet/Common) 以及如何運用它們來剖析命令列。我承認,我特別興奮,最後便會看到,我已經希望自.NET Framework 1.0 的某些項目內建.NET Core 的命令列剖析支援。我所期望,.NET Core 的內建程式庫可能有助於標準化,即使只有一點,命令列之間格式/結構的程式。它不那麼重要標準還有什麼最多可達而不是自己建立的每個人都是,預設的人的慣例。

命令列的慣例

Microsoft.Extensions.CommandLineUtils NuGet 封裝中找不到大量的命令列功能。包含的組件中提供命令列剖析支援選項的簡短和長名稱的 CommandLineApplication 類別、 冒號或等於指定值 (一或多個) 符號,以及符號喜歡-?以取得協助。說到說明,該類別包含自動顯示說明文字的支援。[圖 1 會支援一些範例命令列會顯示。

[圖 1 命令列範例

選項 Program.exe f = Inigo,-l Montoya – hello – 名稱 Princess – 名稱 Buttercup

選項-f 值 「 Inigo 」

選項-l 值 「 Montoya 」

選項-hello 與 「 on 」 的值

選項-具有 「 安娜 」 和 「 Buttercup 」 值的名稱

具有引數的命令 Program.exe"hello","Inigo"、"Montoya","","是","a"、"有幸 」、 「 目標 」,「 符合 」,「 您 」。

命令"hello"

引數"Inigo"

引數"Montoya"

值的引數問候語"It,""is""a,""有幸,""to"「 符合 」,「 您。 」

符號 Program.exe-? 顯示 [說明]

 

如下所述,有多個引數型別,其中稱為 「 引數 」。 參考與命令列組態資料的命令列上指定的值的詞彙引數的多載可能會造成顯著的模稜兩可。因此,文件的其餘部分,我會區分任何種類的泛型引數,指定可執行檔名稱的後面 — 和引數型別稱為 「 引數 」 (大寫) 的大小寫。同樣地,我會分辨其他引數型別,選項和命令,使用大寫,而不是小寫廣泛地參考引數的詞彙。請記下這會是重要的文件其餘。

每個引數類型的描述,如下所示︰

  • 選項 選項會識別為名稱,其中名稱前面會加上單一 (-) 或雙破折號 (-)。選項名稱會以程式設計方式定義使用範本和範本可以包含一或多個下列三個指示︰ 簡短名稱、 完整名稱、 符號。此外,選項可能會有與其相關聯的值。例如,範本可能是"-n |-名稱 |-# < 完整的名稱 > 」 允許以識別任何三個指示項的完整名稱選項。(不過,範本不需要所有三個指示項)。 請注意,它使用單引號或雙引號的虛線,決定是否指定短期或長期的名稱,不論實際名稱的長度。
    若要關聯值的選項,您可以使用空格或指派運算子 (=)。-f = Inigo 和-l Montoya,因此,這兩個指定的選項值的範例。
    如果在範本中使用數字,它們會是短或長名稱,不是符號的一部分。
  • 引數: 引數以它們出現的順序,而不是以名稱來識別。因此,在命令列選項名稱不是前置值是引數。值會對應到哪一個引數根據出現的順序 (選項和命令會被排除在計數)。
  • 命令 命令也提供一組引數和選項。例如,您可以命令名稱"hello"後面接著引數和選項 (或甚至是子命令) 的組合。命令會識別已設定的關鍵字,命令名稱,該命令定義的一部分命令名稱後面的所有值進行群組。

設定命令列

參考.NET 核心 Microsoft.Extensions.CommandLineUtils CommandLineApplication 類別開始之後的程式命令列。這個類別就可以設定每個命令、 選項和引數。當具現化 CommandLineApplication,建構函式會具有選擇性布林值,用來設定要擲回例外狀況 (預設值),如果引數出現尚未特別設定的命令列。

您指定 CommandLineApplication 的執行個體,設定引數使用的選項、 引數,以及命令的方法。例如,假設您想要支援命令列語法,如下所示,其中在方括號中的項目是選擇性的而那些角括弧括住是使用者指定的值或引數︰

Program.exe <-g|--greeting|-$ <greeting>> [name <fullname>] 
     [-?|-h|--help] [-u|--uppercase]

[圖 2 設定基本的剖析功能。

[圖 2 設定命令列

public static void Main(params string[] args)
{
    // Program.exe <-g|--greeting|-$ <greeting>> [name <fullname>]
    // [-?|-h|--help] [-u|--uppercase]
  CommandLineApplication commandLineApplication =
    new CommandLineApplication(throwOnUnexpectedArg: false);
  CommandArgument names = null;
  commandLineApplication.Command("name",
    (target) =>
      names = target.Argument(
        "fullname",
        "Enter the full name of the person to be greeted.",
        multipleValues: true));
  CommandOption greeting = commandLineApplication.Option(
    "-$|-g |--greeting <greeting>",
    "The greeting to display. The greeting supports"
    + " a format string where {fullname} will be "
    + "substituted with the full name.",
    CommandOptionType.SingleValue);
  CommandOption uppercase = commandLineApplication.Option(
    "-u | --uppercase", "Display the greeting in uppercase.",
    CommandOptionType.NoValue);
  commandLineApplication.HelpOption("-? | -h | --help");
  commandLineApplication.OnExecute(() =>
  {
    if (greeting.HasValue())
    {
      Greet(greeting.Value(), names.Values, uppercase.HasValue());
    }
    return 0;
  });
  commandLineApplication.Execute(args);
}
private static void Greet(
  string greeting, IEnumerable<string> values, bool useUppercase)
{
  Console.WriteLine(greeting);
}

一開始的 CommandLineApplication

若要開始,我將個體化 CommandLineApplication,指定是否命令列剖析會嚴格 — throwOnUnexpectedArg 是 true,或比較不嚴謹。如果我指定為非預期的引數時所擲回例外狀況時,所有引數必須明確設定。或者,如果 throwOnUnexpectedArg 為 false,然後設定所無法辨識的任何引數會儲存到 CommandLineApplication.RemainingArguments 欄位。

設定命令和其引數

下一步 [圖 2 正在設定 「 名稱 」 命令。會識別命令的引數清單中的關鍵字是命令函式的第一個參數,名稱。第二個參數是稱為命令會設定成名稱的所有子引數的組態動作 < CommandLineApplication > 委派。在此情況下,都只有一個,別的 CommandArgument 引數使用變數名稱的 「 greeting 」。 不過,很可能會加入額外的引數、 選項和設定委派中的平均子命令。目標參數的委派,CommandLineApplication,此外,有一個回頭指向 commandLineArgument 的父屬性,設定的目標的命令名稱的 CommandLineArgument 的父系。

請注意,設定引數我特別識別的名稱中,它將會支援 multipleValues。如此一來,允許多個指定的值 — 多個名稱在此情況下。這些值會出現下列 「 名稱 」 引數識別碼,直到出現另一個引數或選項的識別項。引數函式的前兩個參數的名稱,參考的引數名稱,所以您可以從清單中的引數,以及描述識別它。

最後一點提醒名稱命令設定中就是您要儲存的傳回引數的函式 (及選項功能,若有的話)。這是必要的因此您可以稍後再擷取引數的名稱相關聯的引數。而不儲存參考,您就必須透過 commandLineApplication.Commands[0 搜尋]。若要擷取的引數資料的引數集合。

儲存命令列的所有資料的其中一個簡潔的方法是將它放入個別的類別從 Asp.net Scaffolding 儲存機制屬性裝飾 (github.com/aspnet/Scaffolding),特別是 src/Microsoft.VisualStudio.Web.CodeGeneration.Core/CommandLine 資料夾。如需詳細資訊,請參閱 「 實作命令列類別與.NET 核心 」 (bit.ly/296SluA)。

設定選項

設定在下一個引數 [圖 2 是選項,它的型別 CommandOption 招呼語。選項的設定是透過選項函式,其中的第一個參數是名為範本的字串參數。請注意,您可以指定三個不同的名稱 (如範例、 $、-g 及問候語) 的選項,而每一個會用來識別引數清單中的選項。此外,範本可以選擇指定下列選項的識別項的角括弧括住名稱透過與其相關聯的值。描述參數之後的選項函式會包含必要的 CommandOptionType 參數。此選項會識別︰

  1. 是否可能選項的識別項之後指定任何值。如果指定 CommandOptionType NoValue,則 CommandOption.Value 函式會設為 「 開啟 」 如果引數清單中會出現的選項。是否有指定值,即使不同的值會指定下列選項的識別項,事實上,會傳回 「 on 」 值。若要查看範例,請檢閱 [大寫] 選項 [圖 2
  2. 或者,如果 CommandOptionType SingleValue 且已指定選項的識別項,但沒有值隨即出現,CommandParsingException 就會擲回識別選項無法識別,因為它不符合範本。換句話說,SingleValue 提供一種檢查所提供的值,假設選項識別項出現。
  3. 最後,您可以提供 CommandOptionType MultipleValue。不過,與命令相關聯的多個值,不同的是多個值在某個選項的情況下允許多次指定相同的選項。比方說,program.exe-名稱 Inigo-Montoya 的名稱。

請注意,任何組態選項會設定讓選項是必要的。而且,事實上,相同的引數,則為 true。為 「 錯誤 」 時若未指定值,您需要檢查是否 HasValue 函式報告錯誤,是否傳回 false。在 CommandArgument,Value 屬性會傳回 null,如果未不指定任何值。若要報告錯誤,請考慮讓使用者享有他們要如何修正問題的詳細資訊,顯示錯誤訊息後面的說明文字。

剖析機制 CommandLineApplication 的另一個重要的行為是區分大小寫。還有,事實上,這次沒有簡單的設定選項,可讓您進行不區分大小寫。因此,您必須變更 CommandLineApplication 所傳入的實質引數的大小寫 (透過 Execute 方法中,當我將在稍後說明) 先達到不區分大小寫。(或者,您可以嘗試提交提取要求在 github.com/aspnet/Common 來啟用此選項。)

顯示說明和版本

內建於 CommandLineApplication 是自動與命令列的設定相關聯的說明文字會顯示 ShowHelp 函式。例如, [圖 3 ShowHelp 輸出會顯示 [圖 2

[圖 3 ShowHelp 顯示輸出

Usage:  [options] [command]
Options:
  -$|-g |--greeting <greeting>  The greeting to display. 
                                The greeting supports a format string 
                                where {fullname} will be substituted 
                                with the full name.
  -u | --uppercase              Display the greeting in uppercase.
  -? | -h | --help              Show help information
Commands:
  name 
Use " [command] --help" for more information about a command.

不幸的是,顯示的說明不會識別是否選項或命令,事實上,選擇性。換句話說,說明文字會假設,並顯示 (透過方括號),所有的選項和命令都是選用。

雖然您可以明確地呼叫 ShowHelp 處理自訂的命令列錯誤,例如它就會自動叫用指定的引數對應 HelpOption 範本。而且,透過 CommandLineApplication.HelpOption 方法的引數來指定 HelpOption 範本。

同樣地,沒有 ShowVersion 顯示之方法的應用程式版本。像 ShowHelp,它已透過兩種方法之一︰

public CommandOption VersionOption(
  string template, string shortFormVersion, string longFormVersion = null).
public CommandOption VersionOption(
  string template, Func<string> shortFormVersionGetter,
  Func<string> longFormVersionGetter = null)

請注意,這兩種方法需要您想要顯示在 VerisionOption 的呼叫中指定的版本資訊。

剖析和讀取命令列的資料

到目前為止我已檢閱詳述如何設定 CommandLineApplication,但我還沒討論曾經讓-關鍵的程序觸發命令列剖析或會發生什麼事緊接剖析引動過程。

若要命令列剖析您的觸發程序需要叫用 CommandLineApplication.Execute 函式和傳遞的命令列上指定的引數清單。在 [圖 1, ,因此它們直接傳遞給 Execute 函數 Main 的引數參數中指定的引數 (請記得先處理區分大小寫並不恰當的大小寫)。它會設定每個引數和選項,設定相關聯的命令列資料的 Execute 方法。

請注意 CommandLineAppliction 包括 OnExecute (Func < int > 叫用),您可以在其中傳遞 < int > Func 委派,會自動執行一次的剖析函式完成。在 [圖 2, ,OnExecute 方法會採用簡單的委派,會檢查 greet 命令已指定叫用 Greet 函式之前。

請注意從叫用委派傳回 int 旨在做為指定來自主要的傳回值。而且,事實上,從叫用傳回任何值會對應到從執行傳回。此外,因為剖析相對比較慢的作業 (我假設它的所有相對),執行支援的多載,Func < 工作 < int >>,如此可讓命令列剖析的非同步引動過程。

方針 命令、 引數和選項

提供可用的三個命令類型,值得快速檢閱使用。

執行 語意識別這類動作在編譯時,使用命令、 匯入或備份。

執行 使用選項來啟用這兩個程式的組態資訊做為整體或特定的命令。

偏好 動詞命令和形容詞或名詞名稱選項的名稱 (例如-色彩、-平行,-projectname)。

不論哪一個引數型別,您設定,請考慮下列指導方針︰

執行 檢閱引數的識別項名稱的大小寫。這可能是指定-FullName 或-fullname 時要尋找命令列的大小寫不同的使用者很容易混淆。

執行 撰寫命令列剖析的測試。執行和 OnExecute 之類的方法可將此很簡單。

執行 當名稱來識別特定的引數是相當麻煩或允許多個值,但前面加上每一個選項識別項是難以使用引數。

考慮 運用 IntelliTect.AssertConsole (itl.tc/CommandLineUtils) 重新導向主控台輸入和輸出,以便插入及擷取主控台,以便進行測試。

還有一個可能的缺點,使用.NET 核心 CommandLineUtils,且它們的英文和未當地語系化。全都放在英文中是顯示的文字,例如 ShowHelp 中找到 (連同通常不當地語系化的例外狀況訊息)。一般來說,這可能不是問題,但是因為命令列是應用程式的使用者介面的一部分,有可能是在英文版的案例是不被接受。基於此理由︰

考慮 ShowHelp 和 ShowHint 撰寫自訂函式,如果當地語系化很重要。

執行 CommandLineApplication 設定為不會擲回例外狀況時,請檢查 CommandLineApplication.RemainingArguments (throwOnUnexpectedArg = false)。

總結

過去三年來在.NET Framework 經歷一些主要的轉換︰

  • 它現在具有跨平台支援,包括 iOS、 Android 和 Linux 的支援 — Wow!!
  • 它完全開啟移轉自開發秘密的專屬途徑,如下所示的開放原始碼 — 模組。
  • 發生重大重構 BCL Api 的.NET 標準程式庫為高度模組化的 (跨) 平台,可利用透過完善的應用程式類型,是否軟體即服務,行動裝置、 在內部,物聯網,桌面等等。
  • 已有 rebirth 的.NET 中,遵循 Windows 8 紀元其中忽略極少的策略或附註的藍圖。

是,比方說,如果您要深入了解新的.NET Core 1.0,尚未開始的所有現在正是要執行這項操作,讓您的最長的時間範圍中要拉長學習曲線。換句話說,如果您打算從舊版升級至它,請加以安裝。答案應該是您將會升級在某個時間點,以及您所做的更快、 快您可以利用新功能。


Mark Michaelis是的 IntelliTect,他擔任其技術架構設計人員和培訓講師的創辦人。近二十他 Microsoft MVP 和 Microsoft 區域經理自已 2007年。Michaelis 服務於多個 Microsoft 軟體設計檢閱小組,包括 C#、 Microsoft Azure、 SharePoint 和 Visual Studio ALM。他在開發人員會議演說並著述的書籍包括他最新、 「 基本 C# 6.0 (第 5 版)。 」在 Facebook 上連絡他 facebook.com/Mark.Michaelis, ,他的部落格上 IntelliTect.com/Mark, ,在 Twitter 上: @markmichaelis 或透過電子郵件地 mark@IntelliTect.com

感謝以下協助校閱本篇文章的技術專家: Phil Spokas 和 Michael Stokesbary