代码编织梦想

简介:对于稍微复杂一些的命令行工具,命令行的提示功能必不可少。那么对于不同语言的开发者,有没有一种简单快捷的实现方式呢?本文分享一种快速实现的方法,使用YAML文件定义命令行工具的使用规范,再通过工具自动生成各种shell的命令行提示脚本,最后分享一些至关重要的命令行解析器。

image.png

不少同学喜欢开发命令行工具,主要是开发快捷,而且和其他命令行工具配合,借助脚本,非常容易实现一些任务的自动化。命令行工具开发比较简单,以Java举一个例子,通常我们只需要一个命令行参数解析器,如Java,就有args4j, jopt,picocli等,转换为结构化的对象,根据输入的参数进行相关的逻辑判断,完成对应的逻辑。其他如Node.js, Deno, Python等,也是一样的流程,都有命令行参数解析器,然后基于命令行输入执行对应的逻辑。

一 命令行提示

如果命令行工具稍微复杂一些,那么必须要提供对应的命令行提示,不然开发者几乎没法使用。举一个例子,阿里云有对应的命令行工具aliyun-cli[1],下载安装后就可以使用aliyun命令行工具了。执行 aliyun --help,会发现非常多的子命令,如果没有命令行工具提示,开发者使用这个工具就非常复杂,要去查文档,或者通过命令行的help来输入命令。

aliyun的命令行工具也提供了对应的代码提示,如下所示:

image.png

这个命令行提示还不错,你只需要选择对应的子命令然后再进行提示就可以了。

大多数开发者喜欢带描述的命令行提示。并不是所有的子命令和命令参数都命名得非常好,如aliyun命令行给出的live子命令提示,大家可能完全不知道这个live是什么(当然,作为阿里云的同学,我还是知道的, live是视频直播)。而像如下包括描述的命令行提示就直观很多:

image.png

二 生成命令行提示

这里不再介绍bash,zsh,fish等各种shell的命令行提示的机制,没有人会手动编写这些命令行提示脚本,大家都会使用框架生成对应shell的命令行提示脚本。

我找了一些命令行解析框架,并且能自动生成命令行提示的,如Java的picocli,Node.js的commander.js,Python的argparse,以及Rust的clap-rs等。我都尝试了一下,最终发现还是clap-rs生成的命令行提示比较好,就是我说的那种带描述,而且还有文件名和目录自动提示,枚举值的提示等,关键是也非常简单。如果有同学有更好的命令行解析框架,希望能留言分享一下。

那么如何让其他语言,如Node.js,Java,Python这些语言编写的命令行工具也能实现和clap-rs的命令行提示一样的效果呢?

三 clap-rs的命令行YAML文件

clap-rs包含了一个命令行工具的YAML规范。我们都知道命令行工具交互比较简单,主要就两个部分:参数和子命令。你看到类似 --conf xxx.yaml 这些带参数名的都属于参数,也可以省略参数名,如 convert a.jpg a.png 其中的a.jpg和a.png也都是参数。子命令就比较容易理解了,我们每天使用的git就是大量使用子命令的,如 git add xxx.jpg 这些。当子命令还可以继续套用子命令,子命令同时也拥有自己的参数。

基于命令行这样的特性,我们完全可以将命令行工具的使用规范通过YAML描述出来,现在一切皆可YAML。

这里我给出一个阿里云命令行工具的YAML定义,当然只是demo。如下:

name: aliyun2
version: "0.1.0"
about: "cli for Alibaba Cloud"
args:
  - version:
      short: v
      long: version
      takes_value: false
      about: Display version
subcommands:
  - oss:
      about: 对象存储
      subcommands:
        - cat:
            about: cat文本文件
            args:
              - file:
                  takes_value: true
                  required: true
                  about: 文件名称
        - ls:
            about: list文件
  - ecs:
      about: 云服务器
      subcommands:
        - SendFile:
            about: send file
        - AddTags:
            about: add tags

可以看出,我首先定义了两个子命令:oss和ecs,然后oss子命令下我又定义了两个子命令:cat和ls。对于oss的cat子命令,我又添加了file这个参数,这样我就可以使用cat来查看oss上文本文件的内容。

有了这个命令行工具YAML规范定义后,我就可以调用clap-rs提供的命令行工具接口,生成对应的shell的提示脚本。效果如下:

image.png

这个命令行提示的效果是不是比原先的要好多了?提示有了描述,选择子命令和参数的时候就简单多了。

四 为所有命令行工具写YAML

讲到这里,相信大家都明白了。无论这个工具是Java,Python,Node.js还是Rust编写的,首先定义好该工具的YAML规范,接下来开发人员根据该规范去编写代码,他可以选择他喜欢的语言,他喜欢的命令行解析器,然后实现对应的功能即可。没有代码提升,编写YAML文件不出错是非常难的,所以我做了一个JSON Schema[2]文件,在编写YAML文件时可以进行代码提示,做到编写命令行YAML规范文件更加简单。JSON Schema的使用方法如下:

image.png

接下来我们会基于该YAML文件,为各种shell生成对应的命令行提示脚本,如bash,zsh,fish和powershell,这样分开后,开发人员也不需要去处理那些他不清楚的命令行提示,或者找该编程语言对应的SDK来做命令行代码提示。如果没有怎么办?即便有了,生成的提示非常简单怎么办?毕竟命令行工具提示非常重要。

相信Node.js的开发者也不希望还要学习一下Rust和clap-rs,这样就太不高效了。因此我又编写了一个工具cli-completion[3], 其主要目的根据上面说的YAML文件帮你自动生成各种shell的命令行提示脚本。来看一下zsh的例子:

$ cli-completion --zsh commands/aliyun2.yaml > /usr/local/share/zsh/site-functions/_aliyun2
$ autoload -U compinit && compinit

再看一下oh-my-zsh的例子:

$ mkdir ~/.oh-my-zsh/custom/plugins/aliyun2 
$ cli-completion --zsh aliyun2.yaml >  ~/.oh-my-zsh/custom/plugins/aliyun2/_aliyun2

通过这种方式,cli-completion可以为任何命令提供命令行提示。也就是说,以后,你只要编写命令行逻辑,关于命令行提示的问题,全部交给cli-completion帮你生成即可。当然考虑到用户体验,你可能需要在命令行工具中,将cli-completion生成的脚本,通过某一子命令,快速同步到客户端环境。

命令行的开发流程:YAML规范编写,命令行提示自动生成,开发人员下班前完成功能实现。

image.png

有同学可能会问,我能否基于YAML文件,并结合某一命令行解析框架,自动完成整个应用的骨架生成,这完全可以,开发人员只要实现一些函数即可,开发会更简单。我个人认为使用PicoCli这些框架自动生成代码,是完全没有问题的。

五 将cli-completion FaaS化

这个功能大家一年都未必用上两次,费时安装也挺麻烦的。现在不是到处都是FaaS,我们也可以尝试一下。首先cli-completion是用Rust编写的,所以可以用传统的方式编写Rust Cloud Lambda,然后部署到云服务上,另外也可以写一个Rust Web应用,如用actix-web,也非常简单。

这些都不够时髦,我们打算将cli-completion的代码WebAssembly化,然后以FaaS方式部署,这里我选择CloudFlare作为FaaS的运行平台。让我们来看一下Demo。

创建一个cli.yaml文件,如下:

name: cli1
version: "0.1.0"
about: "CLI completion for bash, zsh, fish and powershell."
args:
  - help:
      short: h
      long: help
      takes_value: false
      about: Display this help

然后调用cli-completion的FaaS服务,就可以得到对应的命令行提示脚本代码。命令如下:

curl -H 'Content-Type: application/x-yaml' --data-binary "@cli.yaml" https://cli-completion.linux-china.workers.dev/completion/zsh

对比传统的cloud lambda或者cloud function,这种方式FaaS响应速度最快,这种服务调用次数非常少,基本就是每次请求都是冷启动,而WebAssembly这方面就非常有优势。

当然还有一个最大的原因:就是WebAssembly方式的FaaS,它最便宜。

题外话探讨一下cloudflare的WebAssmebly的实现,纯技术讨论,代码如下:

async function handleRequest(request) {
    const { greet } = wasm_bindgen
    await wasm_bindgen(wasm)
    const greeting = greet()
    return new Response(greeting, {status: 200})
}

上述代码中,wasm是一个WebAssembly.Module对象,它是从外部注入的,而不是开发者写的,是FaaS生成的。接下来就是从wasm_bindgen这个函数中获取wasm的导出函数,然后调用 wasm_bindgen(wasm) 将greet函数和wasm module中的export函数进行关联,然后调用greet就会转到wasm module的调用。如果是这样的话,WebAssembly.Module其实是可以外部管理的,当有请求时,再和JavaScript的函数进行关联,这样就可以保证WebAssembly的快速响应。

六 总结

以后大家在写命令行工具时,不用再担心代码提示的问题了。在动手开发工具前,写一下YAML文件,整理和厘清一下你的思路,有哪些子命令,有哪些参数等,然后再基于该YAML文件进行开发,使用什么语言都没有关系,最后配合cli-completion完成命令行提示,你的命令行工具算是相当专业的了,至少从面子上看起来是的 :)

最后列出一些命令行应用涉及的至关重要的命令行解析器,方便大家后续参考:

  • Java:Picocli, JCommander, JOpt, kotlinx-cli, JLine, args4j
  • Node.js:Commander.js, clap.js, minimist, yargs[4]
  • Deno:yargs
  • Python:argparse, docopt, cli-args, clap
  • Golang:argparse, flaggy
  • Rust:clap-rs, pico-args, paw
  • Ruby:cmdparse, commander, GLI
  • C++:gflags, cli, docopt.cpp

整理的不全,欢迎大家补充 :)

相关链接

[1]https://github.com/aliyun/aliyun-cli
[2]https://github.com/linux-china/cli-completion/blob/master/cli-schema.json
[3]https://crates.io/crates/cli-completion
[4]https://www.npmjs.com/search?q=args%20parser

原文链接:https://developer.aliyun.com/article/779826?

版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。 本文链接: https://blog.csdn.net/alitech2017/article/details/110951978

Java基础:String类支持几种构造函数?-爱代码爱编程

String类支持几种构造函数。将创建空String的构造函数称为默认构造函数。例如, String s = new String(); 将创建一个String实例,该实例中不包含字符。 通常希望创建含有初始值的字符串。String类提供了各种构造函数来完成这项功能。使用如下的构造函数可以创建一个被字符数组初始化的字符串(String): St

从大一到大四自学Java编程,我的个人感悟和总结!-爱代码爱编程

    今天我主要分享的就是:我个人在自学编程过程中的得与失,简单来说就是自己的学习感悟,以及总结的编程学习经验,方法和技巧之类的。   同时,我个人会不断的进行自学,掌握更多的编程知识,然后会把自己所学,所领悟到的东西整理编写成适合新手小白学习的入门教程,希望达到的目的就是帮助那些想要学习的新手小白更快更高效的入门。   因为我深知自学编程的不容易,尤其

Leetcode 131-140刷题笔记(非困难题目)-爱代码爱编程

131.分割回文串 该题目使用的是回溯法,在添加到集合前,我们需要判断一下是否为回文串,如果不是则不添加。 在是否为回文串我们可以采用动态规划的思想,对这个字符串进行一个预处理。 //字符串拆分成回文子串 public List<List<String>> partition(String s) {

RocketMQ-概念-角色分析-爱代码爱编程

【RocketMQ学习目录】 RocketMQ-概念-角色分析 【RocketMQ学习目录】1.介绍2.概念图3.角色分析1.Message 消息2.Broker 与 NamerServer 消息处理服务 与 服务寻址3.Producer 生产者4.Consumer 消费者 1.介绍 简介 经过多次天猫双十一的考验 支持 亿级别的消息

Nginx管理 Tomcat服务器负载均衡集群-爱代码爱编程

一、 修改Nginx配置文件: 再次访问Nginx时,将出现tomcat页面;   二、Nginx负载均衡策略   weight可以用于后台服务器性能不均,可以手动指定服务器性能好的权重高一些,也就是weight的值比较高。   此时将程序war包分别放到tomcat服务器下面执行,前端再有请求后,就会通过Nginx完成负载,分别到不

如何无缝迁移 SpringCloud/Dubbo 应用到 Serverless 架构-爱代码爱编程

作者 | 行松 阿里巴巴云原生团队 来源 | Serverless 公众号,整理自《Serverless 技术公开课》 背景 通过前面几节课程的学习,相信大家对于 SAE 平台已经有了一定的了解,SAE 基于 IaaS 层资源构建的一款 Serverles 应用托管产品,免除了客户很多复杂的运维工作,开箱即用、按用量付费;并且提供了丰富的 Op

小白都能学会的新建数据库,无非就是这七招-爱代码爱编程

数据库的基本操作,无非就是下面7招: 创建 读取 插入 更新 查询 排序 删除 我们先要引入sqlite3这个模块,这个模块是Python自带的,不需要安装(上一篇我写错了,好多好心的小伙伴提醒我,非常感谢纠正),然后创建数据库很简单,我们假设我们要把下面一个“工资单”表格数据存入数据库,如何操作呢,下面一步一步来操作,follow me

PyTorch中的contiguous-爱代码爱编程

参考:https://zhuanlan.zhihu.com/p/64551412 Pytorch中Tensor的底层存储方式:存在一个一维数组中经映射后变成形式上的矩阵 那么如何把一维数组映射为矩阵呢? Stride 原一维数组为[1,2,3,4,5,6,7,8,9] eg. Stride = (3,1) 则代表行之间的距离为3,列之间的距离为1,即

Python统计某一只股票每天的开盘,收盘,最高,最低价格!-爱代码爱编程

模块:Numpy 码字不易,转载请注明出处!十分感谢! 准备工作: 抓取某一只股票的信息,每30min为一组数据,可以参考上一篇: Note: 只为演示如何统计,更精准的可以抓取每5min为一组数据 PS:如有需要Python学习资料的小伙伴可以加点击下方链接自行获取 python免费学习资料以及群交流解答点击即可加入 如何获取股票原始数据

Django:DjangoRestFramework drf 开发3-爱代码爱编程

7. 反序列化 反序列化有 验证、保存 两个步骤。 1. 验证 实现步骤: 定义序列化器类型,根据模型类定义、写属性、类型、参数创建序列化器对象,以请求报文中的字典为参数调用 is_valid() 方法,如果验证通过则返回True,验证失败返回False验证通过时可以调用 validated_data 获取验证后的数据验证失败可以通过 error_

Jenkins配置生成测试报告和发送邮件-爱代码爱编程

Jenkins配置生成测试报告和发送邮件 前言 Web自动化测试中,我们需要实现定时自动化,并且自动生成测试报告,通过邮件形式发送到指定人员,现在就来讲解怎么通过Jenkins实现生成测试报告并发送测试报告邮件方法。 二、使用步骤 (1) 代码设置项 1.下载BeautifulReport模块 下载地址:https://pan.baidu.com/

python练习(二)-爱代码爱编程

#字符串通配符 """ 题目描述 问题描述:在计算机中,通配符一种特殊语法,广泛应用于文件搜索、数据库、正则表达式等领域。 现要求各位实现字符串通配符的算法。 要求: 实现如下2个通配符: *:匹配0个或以上的字符(字符由英文字母和数字0-9组成,不区分大小写。下同) ?:匹配1个字符 输入: 通配符表达式; 一组字符串。 输出: 返回匹配的结果,正确输出