.NET 小技 - 命令列參數應用
|
|
2 |
|
![]() |
.NET Core 程式寫久了,用命令列參數控制程式執行已是日常,像發佈 .NET 6 程式時便少不了 --no-self-contained、-c Release、-r win-x64 等參數,而啟動 ASP.NET Core 時,使用 --urls 指定 HTTP Port 更是必備技巧。
.NET 程式接收 .exe 啟動參數不難,由 void Main(string[] args) 取出 args 進行解析即可,但要做到參數可有可無,不限順序的彈性得花點功夫,一般不需要自己造輪子,多會借助 System.CommandLine、Command Line Parser Library... 等程式庫。
而在 .NET Core 程式裡,命令列參數還有個重要用途 - 作為 IConfiguration 的設定來源之一。ASP.NET Core 的 IConfiguration 支援多種來源,預設可透過 appsettings.json、appsettings..json、環境變數、命令列參數等方式設定,並有「重複設定以後者為準」的順序概念,可以做到在 appsettings.json 給預設值,實際部署時以環境變數為準,遇特別狀況可手動執行以命令列參數覆寫設定,應用起來非常有彈性。
而透過命令列參數提供 IConfiguration 設定值的做法,也可應用在一般的 Console 程式,例如:
using System.Text.Json;
using Microsoft.Extensions.Configuration;
//dotnet run -- param1=value1 --param2 value2 /param3 value3
//三種格式可同並存,但不建議混用,應統一用 =、-- 或 /
Console.WriteLine($"args.Length = {args.Length}");
Console.WriteLine($"args = {JsonSerializer.Serialize(args)}");
IConfiguration config = new ConfigurationBuilder()
.AddCommandLine(args)
.Build();
foreach (var s in config.AsEnumerable()) {
Console.WriteLine($"* {s.Key} = {s.Value}");
}
Console.WriteLine(config.GetValue<string>("param1", "N/A"));
Console.WriteLine(config.GetValue<string>("param4", "N/A"));
我自己在寫 ASP.NET Core 時,有時會設計成靠命令列參數執行一次性作業,例如:在 EF Core 資料庫寫入範例資料:
var sqliteDbPath = Path.Combine(builder.Environment.ContentRootPath, "static-files.sqlite");
builder.Services.AddDbContext<StaticFileDbContext>(options =>
{
options.UseSqlite($"data source={sqliteDbPath}");
});
builder.Services.AddScoped<StaticFileDbRepository>();
var app = builder.Build();
// init db
if (!File.Exists(sqliteDbPath))
{
using (var scope = app.Services.CreateScope())
{
scope.ServiceProvider.GetRequiredService<StaticFileDbContext>()
.Database.Migrate();
}
}
// insert the demo data on-demand
if (app.Configuration.GetValue<string>("insertDemoData", "false") == "true")
{
using (var scope = app.Services.CreateScope())
{
var rsp = scope.ServiceProvider.GetRequiredService<StaticFileDbRepository>();
var userId = "jeffrey";
var clientIp = "::1";
rsp.UpdateFile("/index.html", Encoding.UTF8.GetBytes("<html><body><link href=css/site.css rel=stylesheet /> Web in JSON</body></html>"), userId, clientIp);
rsp.UpdateFile("/css/site.css", Encoding.UTF8.GetBytes("body { font-size: 9pt }"), userId, clientIp);
}
當需要展示資料測試或展示時,加上參數執行 DemoWeb.exe --insertDemoData true
即可在資料庫新增展示資料。
不過,透過 IConfiguration 讀取命令列參數格式比較死板,只接受名稱對映值的寫法(--paramName paramValue),沒法提供短版名稱(如:--help 可寫成 -h)、也不支援開關式參數(如:--no-self-contained)。
若有 Console 程式有較複雜或彈性的參數樣式需求,需回歸 System.CommandLine、Command Line Parser 等專門程式庫才有完整支援。這裡用微軟的 System.CommandLine 來練習,由於它仍在 Beta 階段,參照時需加上 --prerelease 參數 dotnet add package System.CommandLine --prerelease
。
using System.CommandLine;
var cmd = new RootCommand
{
Description = "參數測試程式",
//命令及參數解析失敗時視為錯誤
TreatUnmatchedTokensAsErrors = true
};
var inputOption = new Option<FileInfo>(
aliases: new[] { "--input", "-i" },
description: "檔案路徑") {
IsRequired = true
};
cmd.AddOption(inputOption);
var colorOption = new Option<bool>(
aliases: new[] { "--color", "-c" },
description: "彩色顯示"
);
cmd.AddOption(colorOption);
cmd.SetHandler((FileInfo input, bool color) => {
Console.WriteLine(input.FullName);
Console.WriteLine(color);
}, inputOption, colorOption);
return cmd.Invoke(args);
System.CommandLine 支援的命令、參數格式變化很多,細節可參考官方文件。
【參考資料】
- Configuration in .NET
- Command-line configuration provider
- Tutorial: Get started with System.CommandLine
Tips of using command line argument for logic control in .NET 6.
Comments
# by Huang
命令列參數執控制程式 是否是要輸入參數"值"
# by Jeffrey
to Huang, 謝,已修正。