閑話
在很久很久以前,電腦是命令行/終端/控制臺的天下,那屏幕上的光標在行雲流水般的鍵盤敲擊下歡快地飛躍著,那一行行的字符輸出唰唰唰地滾動著……直到 Windows 95 的出現(那時候我還不知道蘋果電腦和它的操作系統),我的鼠標終於不再召灰,開始有了用武之地,然後就是 GUI 的天下……
然而世事就是這樣,錦繡繁華之後就開始返璞歸真,大魚大肉太多就向往點粗茶淡飯,開車開久了就懷念起自行車,GUI 充斥的 Windows 的世界裏似乎也開始掛起一陣控制臺的清風。畢竟,一旦你熟悉了各種命令和參數,敲鍵盤的速度還是勝過鼠標的,只是現在的人都太懶或者太忙,總是寧願犧牲效率而不願意去多記一點東西。
我個人還是很喜歡命令行的,尤其是遠程訪問一個系統的時候,一個簡單的 ssh 命令直接登錄到遠程 linux,一個簡單的 scp 命令就可以互相傳輸文件,這種便利、快捷是 Windows 遠程桌面所無法比擬的。GUI 也許是 Windows 的設計哲學, 做什麽事情都要靠 GUI。 沒錯,這大大降低了各種操作門檻,但是作為一個程序員,GUI 工具並非總是最佳選擇,但除了 GUI 工具,替代選擇並不多——直到 .NET Core 的出現。
專業的控制臺程序
首先我們要有個標準,怎樣才算“專業的控制臺程序”?
平常無論是寫著玩還是工作需要,我都做過一些控制臺程序,在啟動參數的傳入、解析和執行上都比較隨意,類似 MyProgram abc 123
這樣,MyProgram
是程序名,abc
、123
是參數值,內部直接用 args[0]
、args[1]
取得參數值並使用。僅此而已。時間長了,自己都搞不懂每個參數什麽意思,參數有哪些有效值,都得查源代碼才知道,每個參數的順序也很重要,顛倒不得。而內部實現上,至少是 Main
方法裏是典型的面條式代碼。由此可見,我的這些控制臺程序,無論是外在,還是內在,都業余得很。
那一個專業的控制臺程序應該是什麽樣的呢?
完善的幫助信息
當你面對一個陌生的命令行程序,或者重新面對你自己2個月之前寫的命令行程序,你心裏第一反應會是什麽呢?讓我猜猜,你的第一反應一定是“這貨到底怎麽用?”(不要告訴我我猜錯了,我不相信??),下一個想法就是這個程序能告訴我用法就好了。一個專業的控制臺程序必然會滿足你的這個需要——它可以提供完善的幫助信息。比如 git:
有了這些幫助信息,我們自然信心倍增,心裏有譜多了。
我希望我寫的控制臺程序也能做到這點!
符合“國際慣例”的調用方式
如果你留意一下 Linux 平臺下的一眾控制臺程序,你會發現他們的參數組織和調用方式十分類似,這種約定俗成的“國際慣例”十分有助於降低熟悉各個控制臺程序的學習成本。
我們還是以 git 為例,從上面的截圖可以看出,它有非常多的參數可用。雖然都是參數,但根據作用不同,可以分為 command, argument, option 三類。我不是控制臺程序達人,對這3類參數的區別與聯系還在深入理解、學習中。目前的理解是(以 git 為例):
command
一個復雜的控制臺程序可以提供多個子命令,而 command 就代表這些子命令
比如
clone
,init
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個月後只有“上帝”明白的效果。隨著程序參數增多、邏輯越來越復雜,這麽搞下去,“上帝”依然可以很瀟灑,我會被搞死的。為了不讓我變成禿頭,為了我可以有更多的時間玩遊戲,控制臺程序的內部實現必須井井有條、易於維護!
問題來了
了解了一個專業的控制臺程序應具備的素質,那麽接下來有一個問題縈繞在我心頭久久不肯散去……
作為一個小白,要實現一個有詳細說明信息、調用方式符合“國際慣例”、還能優雅地處理各種參數的控制臺程序何其困難?而如此套路化的東西難道沒有一套成型的東西供參考嗎?
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
的默認值是0maxValue
的默認值是100它可以設置生成的算式個數,這也只是調整輸出結果,因此我也將其歸為 option:
count
count
的默認值是 10
MathForKids 程序的用法
在我們看代碼之前,先看看這個程序用起來應該是什麽樣的。
註意:在本文發布之時,.NET Core 處於 RC 2 階段,還不支持編譯為本地可執行文件。所以目前必須使用 dotnet MathForKids.dll
來運行。圖中紅色下劃線表示輸入的命令
首先,它可以提供幫助信息:
從幫助信息中我們可以看到它支持的所有參數,並且支持參數的全寫和簡寫,比如我們可以寫 --minValue
也可以簡寫為 -min
。
當我們調用它只生成加法和乘法,其它選項默認時:
註意,我們一次傳入了多個參數: 加 乘
。
當我們設置一些 option 來改變輸出結果時:
註意,這裏演示了使用全寫和簡寫添加 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;
}
}
}
CommandArgument
和 CommandOption
的使用非常簡單,有 HasValue
和 Value
等方法可以判斷和取值。
所有代碼放在我的 GitHub 裏。
官方的測試類也是一個很好的參考資源。
Tags: Windows 返璞歸真 蘋果電腦 自行車 控制臺
文章來源: