流水沉微

CLI App in C# 其二

这是 CLI App in C# 系列的第二篇,本篇主要介绍gen的开发过程。教程里的所有代码参见keaising/gen

新建项目

  1. 在合适的地方使用dotnet new console -n gen新建一个名字是gen的控制台项目
  2. 通过命令行工具cd到项目目录下
  3. 输入dotnet run运行该项目,应当能看到Hello World!

如下图所示:

new console app

添加引用

在项目目录下输入如下命令,添加对CommandLineUtils项目的引用

dotnet add package McMaster.Extensions.CommandLineUtils

这一步是通过命令行的方式引入nuget包,也可以在Visual Studio里进行nuget包管理,效果完全一样

添加完成之后,项目文件gen.csproj可见ItemGroup中已经有了对CommandLineUtils的引用

<Project Sdk="Microsoft.NET.Sdk">
  <PropertyGroup>
    <OutputType>Exe</OutputType>
    <TargetFramework>netcoreapp2.2</TargetFramework>
  </PropertyGroup>

  <ItemGroup>
    <PackageReference Include="McMaster.Extensions.CommandLineUtils" Version="2.3.4" />
  </ItemGroup>

</Project>

改造Programs文件

此时应用还只能输出Hello World!,根据CommandLineUtils文档稍做改造,实现输出应用信息的功能。修改代码如下:

using System;
using McMaster.Extensions.CommandLineUtils;

namespace gen
{
    [Command(Name = "gen", Description = "A simple console app.")]
    class Program
    {
        public static int Main(string[] args)
            => CommandLineApplication.Execute<Program>(args);

        private int OnExecute(CommandLineApplication app, IConsole console)
        {
            app.ShowHelp();
            return 0;
        }
    }
}

可以通过dotnet rundotnet run -- -h直接运行应用和查看帮助信息,如下图:

help info

生成手机号

添加一个Mobile.cs文件,实现生成一个随机手机号的功能

using McMaster.Extensions.CommandLineUtils;
using System.Linq;
using System;

[Command("mobile", Description = "generate mobile numbers.")]
public class Mobile
{
    private static readonly Random rand = new Random();
    public void OnExecute(IConsole console)
    {
        console.WriteLine(GenMobile());
    }

    string GenMobile()
    {
        var second = rand.Next(3, 9);
        var tail = Enumerable.Range(1, 9).Select(r => rand.Next(0, 9));
        return $"1{second}{string.Join("", tail)}";
    }
}

此外还需要在Programs.csProgram类的标签上加上Mobile的作为SubCommand:

[Command(Name = "gen", Description = "A simple console app."), Subcommand(typeof(Mobile))]

现在就可以使用mobile作为子命令产生随机手机号了

$ dotnet run -- mobile
$ 13137575348

生成身份证号

同理,添加一个Id.cs文件用于生成身份证号,实现逻辑与Mobile.cs类似,不再赘述。

不同的是,为了支持身份证号类型和身份证号开头,需要使用ArgumentOption两个Attribute

身份证号码类型

此处通过引入Option,来实现在命令后增加 -l或者 -s来区分18位和15位的身份证号,

[Option(ShortName = "l", LongName = "eighteen", ShowInHelpText = true, Description = "18位身份证号")]
public bool Long { get; set; }

[Option("-s|--fifteen", Description = "15位身份证号")]
public bool Short { get; set; }

当用户输入gen id -l时,bool类型的Long字段的值为trueShort字段没有处理,值是默认值false,以此为据进行分支选择,代码如下:

public void OnExecute(IConsole console)
{
	if (Long)
	{
		console.WriteLine(genLong());
	}
	else if (Short)
	{
		console.WriteLine(genShort());
	}
}

此外,Option还有多种写法和多个选项可供配置,详情参阅文档

身份证号码开头

为了支持用户输入的内容作为身份证号码的开头,需要引入一个参数StartWith

[Argument(0, Description = "The start of ID card number.", Name = "Start")]
public string StartWith { get; }

Start参数为Id子命令的第一个参数(0),名字是Start,代码里对应的是StartWith字段,代码如下:

private string genLong()
{
	var r = new Random();
	var myValues = new int[] { 1, 2, 3, 4, 5, 6, 7, 8, 9, 0 }; 
	var head = Enumerable.Range(0, 6 - StartWith.Length).Select(e => myValue[r.Next(myValues.Length)]);
	var year = r.Next(1970, 2018);
	var month = $"{r.Next(1, 12)}".PadLeft(2, '0');
	var day = $"{r.Next(1, 28)}".PadLeft(2, '0');
	var tail = Enumerable.Range(0, 4).Select(e => myValues[r.Nex(myValues.Length)]);
	return StartWith + string.Join("", head) + year + month + day + string.Join("", tail);
}

调试 & 运行

至此,命令行工具gen的所有功能都已经实现,可以通过以下几个命令来调用功能和调试


.\gen>  //查看gen的帮助和版本信息
.\gen>  dotnet run -- -h

.\gen>  //查看gen mobile的帮助信息
.\gen>  dotnet run -- mobile -h

.\gen>  //运用gen mobile, 生成一个随机手机号
.\gen>  dotnet run -- mobile 
.\gen>  15810458877

.\gen>  //查看gen id的帮助信息
.\gen>  dotnet run -- id -h

.\gen>  //运行gen id, 生成一个随机的18位身份证号
.\gen>  dotnet run -- id -l

.\gen>  //运行gen id, 110为开头, 生成一个随机的15位身份证号
.\gen>  dotnet run -- id -s 110

此外,也可以通过配置Visual Studio的Debug参数实现在VS里运行和实时调试,例如要运行gen id -s 100,只需要在 Project Property -> Debug -> Application arguments里填写id -s 100即可,如下图

project property

つづく