使用.Net Core编写命令行工具(CLI)
使用.Net Core编写命令行工具(CLI)
命令行工具(CLI)
命令行工具(CLI)是在图形用户界面得到普及之前使用最为广泛的用户界面,它通常不支持鼠标,用户通过键盘输入指令,计算机接收到指令后,予以执行。
通常认为,命令行工具(CLI)没有图形用户界面(GUI)那么方便用户操作。因为,命令行工具的软件通常需要用户记忆操作的命令,但是,由于其本身的特点,命令行工具要较图形用户界面节约计算机系统的资源。在熟记命令的前提下,使用命令行工具往往要较使用图形用户界面的操作速度要快。所以,图形用户界面的操作系统中,都保留着可选的命令行工具。
另外,命令行工具(CLI)应该是一个开箱即用的工具,不需要安装任何依赖。
一些熟悉的CLI工具如下:
1. dotnet cli
2. vue cli
3. angular cli
4. aws cli
5. azure cli
指令设计
本文将使用.Net Core(版本3.1.102)编写一个CLI工具,实现配置管理以及条目(item)管理(调用WebApi实现),详情如下:
框架说明
编写CLI使用的主要框架是CommandLineUtils,它主要有以下优势:
1. 良好的语法设计
2. 支持依赖注入
3. 支持generic host
WebApi
提供api让cli调用,实现条目(item)的增删改查:
[Route("api/items")]
[ApiController]public class ItemsController : ControllerBase
{ private readonly IMemoryCache _cache; private readonly string _key = "items"; public ItemsController(IMemoryCache memoryCache){_cache = memoryCache;}[HttpGet] public IActionResult List(){ var items = _cache.Get<List<Item>>(_key); return Ok(items);}[HttpGet("{id}")] public IActionResult Get(string id){ var item = _cache.Get<List<Item>>(_key).FirstOrDefault(n => n.Id == id); return Ok(item);}[HttpPost] public IActionResult Create(ItemForm form){ var items = _cache.Get<List<Item>>(_key) ?? new List<Item>(); var item = new Item{Id = Guid.NewGuid().ToString("N"),Name = form.Name,Age = form.Age};items.Add(item);_cache.Set(_key, items); return Ok(item);}[HttpDelete("{id}")] public IActionResult Delete(string id){ var items = _cache.Get<List<Item>>(_key); var item = items?.SingleOrDefault(n => n.Id == id); if (item == null){ return NotFound();}items.Remove(item);_cache.Set(_key, items); return Ok();}
}
CLI
Program - 函数入口
[HelpOption(Inherited = true)] //显示指令帮助,并且让子指令也继承此设置
[Command(Description = "A tool to communicate with web api"), //指令描述Subcommand(typeof(ConfigCommand), typeof(ItemCommand))] //子指令class Program
{ public static int Main(string[] args){//配置依赖注入 var serviceCollection = new ServiceCollection();serviceCollection.AddSingleton(PhysicalConsole.Singleton);serviceCollection.AddSingleton<IConfigService, ConfigService>();serviceCollection.AddHttpClient<IItemClient, ItemClient>(); var services = serviceCollection.BuildServiceProvider(); var app = new CommandLineApplication<Program>();app.Conventions.UseDefaultConventions().UseConstructorInjection(services); var console = (IConsole)services.GetService(typeof(IConsole)); try{ return app.Execute(args);} catch (UnrecognizedCommandParsingException ex) //处理未定义指令{console.WriteLine(ex.Message); return -1;}}//指令逻辑 private int OnExecute(CommandLineApplication app, IConsole console){console.WriteLine("Please specify a command.");app.ShowHelp(); return 1;}
}
2. ConfigCommand和ItemCommand - 实现的功能比较简单,主要是指令描述以及指定对应的子指令
[Command("config", Description = "Manage config"),Subcommand(typeof(GetCommand), typeof(SetCommand))]public class ConfigCommand
{ private int OnExecute(CommandLineApplication app, IConsole console){console.Error.WriteLine("Please submit a sub command.");app.ShowHelp(); return 1;}
}[Command("item", Description = "Manage item"),Subcommand(typeof(CreateCommand), typeof(GetCommand), typeof(ListCommand), typeof(DeleteCommand))]public class ItemCommand
{ private int OnExecute(CommandLineApplication app, IConsole console){console.Error.WriteLine("Please submit a sub command.");app.ShowHelp(); return 1;}
}
3. ConfigService - 配置管理的具体实现,主要是文件读写
public interface IConfigService
{ void Set();Config Get();
}public class ConfigService: IConfigService
{ private readonly IConsole _console; private readonly string _directoryName; private readonly string _fileName; public ConfigService(IConsole console){_console = console;_directoryName = ".api-cli";_fileName = "config.json";} public void Set(){ var directory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), _directoryName); if (!Directory.Exists(directory)){Directory.CreateDirectory(directory);} var config = new Config{//弹出交互框,让用户输入,设置默认值为http://localhost:5000/Endpoint = Prompt.GetString("Specify the endpoint:", "http://localhost:5000/")}; if (!config.Endpoint.EndsWith("/")){config.Endpoint += "/";} var filePath = Path.Combine(directory, _fileName); using (var outputFile = new StreamWriter(filePath, false, Encoding.UTF8)){outputFile.WriteLine(JsonConvert.SerializeObject(config, Formatting.Indented));}_console.WriteLine($"Config saved in {filePath}.");} public Config Get(){ var filePath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), _directoryName, _fileName); if (File.Exists(filePath)){ var content = File.ReadAllText(filePath); try{ var config = JsonConvert.DeserializeObject<Config>(content); return config;} catch{_console.WriteLine("The config is invalid, please use 'config set' command to reset one.");}} else{_console.WriteLine("Config is not existed, please use 'config set' command to set one.");} return null;}
}
4. ItemClient - 调用Web Api的具体实现,使用HttpClientFactory的方式
public interface IItemClient
{Task<string> Create(ItemForm form);Task<string> Get(string id);Task<string> List();Task<string> Delete(string id);
}public class ItemClient : IItemClient
{ public HttpClient Client { get; } public ItemClient(HttpClient client, IConfigService configService){ var config = configService.Get(); if (config == null){ return;}client.BaseAddress = new Uri(config.Endpoint);Client = client;} public async Task<string> Create(ItemForm form){ var content = new StringContent(JsonConvert.SerializeObject(form), Encoding.UTF8, "application/json"); var result = await Client.PostAsync("/api/items", content); if (result.IsSuccessStatusCode){ var stream = await result.Content.ReadAsStreamAsync(); var item = Deserialize<Item>(stream); return $"Item created, info:{item}";} return "Error occur, please again later.";} public async Task<string> Get(string id){ var result = await Client.GetAsync($"/api/items/{id}"); if (result.IsSuccessStatusCode){ var stream = await result.Content.ReadAsStreamAsync(); var item = Deserialize<Item>(stream); var response = new StringBuilder();response.AppendLine($"{"Id".PadRight(40, ' ')}{"Name".PadRight(20, ' ')}Age");response.AppendLine($"{item.Id.PadRight(40, ' ')}{item.Name.PadRight(20, ' ')}{item.Age}"); return response.ToString();} return "Error occur, please again later.";} public async Task<string> List(){ var result = await Client.GetAsync($"/api/items"); if (result.IsSuccessStatusCode){ var stream = await result.Content.ReadAsStreamAsync(); var items = Deserialize<List<Item>>(stream); var response = new StringBuilder();response.AppendLine($"{"Id".PadRight(40, ' ')}{"Name".PadRight(20, ' ')}Age"); if (items != null && items.Count > 0){ foreach (var item in items){response.AppendLine($"{item.Id.PadRight(40, ' ')}{item.Name.PadRight(20, ' ')}{item.Age}");}} return response.ToString();} return "Error occur, please again later.";} public async Task<string> Delete(string id){ var result = await Client.DeleteAsync($"/api/items/{id}"); if (result.IsSuccessStatusCode){ return $"Item {id} deleted.";} if (result.StatusCode == HttpStatusCode.NotFound){ return $"Item {id} not found.";} return "Error occur, please again later.";} private static T Deserialize<T>(Stream stream){ using var reader = new JsonTextReader(new StreamReader(stream)); var serializer = new JsonSerializer(); return (T)serializer.Deserialize(reader, typeof(T));}
}
如何发布
在项目文件中设置发布程序的名称(AssemblyName):
<PropertyGroup><OutputType>Exe</OutputType><TargetFramework>netcoreapp3.1</TargetFramework><AssemblyName>api-cli</AssemblyName></PropertyGroup>
进入控制台程序目录:
cd src/NetCoreCLI
发布Linux使用版本:
dotnet publish -c Release -r linux-x64 /p:PublishSingleFile=true
发布Windows使用版本:
dotnet publish -c Release -r win-x64 /p:PublishSingleFile=true
发布MAC使用版本:
dotnet publish -c Release -r osx-x64 /p:PublishSingleFile=true
使用示例
这里使用Linux作为示例环境。
1. 以docker的方式启动web api
2. 虚拟机上没有安装.net core的环境
3. 把编译好的CLI工具拷贝到虚拟机上,授权并移动到PATH中(如果不移动,可以通过./api-cli的方式调用)
sudo chmod +x api-cli #授权sudo mv ./api-cli /usr/local/bin/api-cli #移动到PATH
4. 设置配置文件:api-cli config set
5. 查看配置文件:api-cli config get
6. 创建条目:api-cli item create
7. 条目列表:api-cli item list
8. 获取条目:api-cli item get
9. 删除条目:api-cli item delete
10. 指令帮助:api-cli -h, api-cli config -h, api-cli item -h
11. 错误指令:api-cli xxx
源码地址
https://github.com/ErikXu/NetCoreCLI
参考资料
https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#using-rids](https://docs.microsoft.com/en-us/dotnet/core/rid-catalog#using-rids
https://medium.com/swlh/build-a-command-line-interface-cli-program-with-net-core-428c4c85221
关注架构师高级俱乐部
开启架构之路
使用.Net Core编写命令行工具(CLI)相关推荐
- java 编写命令行工具_编写命令行工具
1.使用common-cli编写命令行工具 commons-cli是Apache开源组织提供的用于解析命令行参数的包. 先引用common-cli依赖包: commons-cli commons-cl ...
- python工具是什么-使用Python编写命令行工具有什么好的库?
使用Python编写命令行工具的库很多,我最推荐的还是Google Fire Hello World 要介绍Fire是什么,看一个简单的例子就明白了 # calc.py import fire cla ...
- go编写命令行工具_编写者的命令行文档转换工具
go编写命令行工具 今天,我们有足够的工具可用于在我们的计算机上编辑备忘录,信件,论文,书籍,演示幻灯片和其他文档. 这既有好处也有缺点:一方面,如果您不喜欢某个软件,则可以随时随地转到另一个软件上: ...
- swift编写命令行工具
2019独角兽企业重金招聘Python工程师标准>>> 原文: https://www.raywenderlich.com/128039/command-line-programs- ...
- node工程默认url_node命令行工具之实现项目工程自动初始化的标准流程
一.目的 传统的前端项目初始流程一般是这样: 可以看出,传统的初始化步骤,花费的时间并不少.而且,人工操作的情况下,总有改漏的情况出现.这个缺点有时很致命. 甚至有马大哈,没有更新项目仓库地址,导致提 ...
- python3命令需要使用命令行开发者工具_3 个 Python 命令行工具
用 Click.Docopt 和 Fire 库写你自己的命令行应用. 有时对于某项工作来说一个命令行工具就足以胜任.命令行工具是一种从你的 shell 或者终端之类的地方交互或运行的程序.Git 和 ...
- 中文 Markdown 编写格式规范的命令行工具 lint-md
lint-md 用于检查中文 markdown 编写格式规范的命令行工具,基于 AST 开发,且方便集成 ci.Cli tool to lint your markdown file for Chin ...
- Jenkins CLI命令行工具,助你轻松管理 Jenkins
Jenkins CLI,简称 jcli,一个使用 Golang 开发的开源的 Jenkins 命令行工具.它可以帮忙你轻松地管理 Jenkins.无论你是 Jenkins 插件开发者,还是 Jenki ...
- 使用 Apache Commons CLI 开发命令行工具
http://www.ibm.com/developerworks/cn/java/j-lo-commonscli/index.html 使用 Apache Commons CLI 开发命令行工具 杨 ...
最新文章
- eclipse占用内存过大_MySQL 服务占用cpu 100%,如何排查问题? (MySQL面试第七弹)...
- 通过JAVA对HDFS进行操作管理插件
- ubuntu配置GDB
- 青苹果一键重装系统安装VS2015
- 【报告分享】线上汉服消费洞察报告.pdf(附下载链接)
- 微信公众号开发系列教程一(调试环境部署续:vs远程调试)
- 17年北邮计算机应用基础,2017计算机应用基础考试题及答案
- memmove、memcpy和memccpy简介
- 软件开发模型_为什么越来越多软件开发团队都放弃了瀑布模型?
- SQL语法中的JOIN类型
- oracle 的insert into的详解
- 拒绝焦虑状态:TA爱我吗?
- python orm框架
- mybatis 绑定失败:Invalid bound statement (not found): com.demo.service.api.dao.SysUserMapper.insert
- webpack 的安装与使用
- JavaScript学习笔记|数据类型——Object类型、for in循环
- [HNOI2005]狡猾的商人 差分约束+判环
- (NCRE网络技术)中小型网络系统总体规划与设计方法-知识点
- VSCode 开启||关闭右侧预览功能
- botpress搭建智能问答机器人