阿里妹攻略:稍微复杂一点的命令行工具,命令行的提示功能必不可少。那么对于不同语言的开发者来说,有没有简单快速的实现方式呢?本文分享一个快速实现的方法,使用YAML文件定义命令行工具的使用规范,然后通过工具自动生成各种shell的命令行提示脚本,最后分享一些关键的命令行解析器。很多同学喜欢开发命令行工具,主要是开发速度快,配合其他命令行工具。在脚本的帮助下,自动化一些任务是非常容易的。命令行工具的开发比较简单。以Java为例。通常我们只需要一个命令行参数解析器,比如Java,有args4j、jopt、picocli等,转换成结构化对象,与输入参数相关。逻辑判断,完成相应的逻辑。其他的,比如Node.js、Deno、Python等,也有相同的流程,都有命令行参数解析器,然后根据命令行输入执行相应的逻辑。命令行提示如果命令行工具再复杂一点,那么就必须提供相应的命令行提示,否则开发者几乎无法使用。举个例子,阿里云有对应的命令行工具aliyun-cli[1]。下载安装后,就可以使用aliyun命令行工具了。执行aliyun--help,会发现很多子命令。如果没有命令行工具提示,开发者使用这个工具会很复杂。您需要查看文档或通过帮助命令行输入命令。阿里云的命令行工具也提供了相应的代码提示,如下图:这个命令行提示还不错,只需要选择相应的子命令,然后提示即可。大多数开发人员更喜欢带有说明的命令行提示。并不是所有的子命令和命令参数都命名的很好,比如aliyun命令行给出的live子命令提示,你可能不知道这个live是什么(当然,作为阿里云的同学,我还是知道的,live是一个视频直播)。下面介绍的命令行提示就直观多了:2.生成命令行提示bash、zsh、fish等各种shell的命令行提示机制这里不再介绍。没有人会手动编写这些命令行提示脚本。该框架将用于生成shell对应的命令行提示脚本。我发现了一些可以自动生成命令行提示的命令行解析框架,比如Java中的picocli,Node.js中的commander.js,Python中的argparse,Rust中的clap-rs。都试过了,最后发现clap-rs生成的命令行提示比较好,就是我用description说的那种,还有文件名和目录的自动提示,枚举值的提示等。关键就是它也很简单。.如果有同学有更好的命令行解析框架,希望能留言分享。那么用Node.js、Java、Python等其他语言编写的命令行工具如何才能达到clap-rs命令行提示一样的效果呢?三个clap-rs命令行YAML文件clap-rs包含一个命令行工具YAML规范。我们都知道命令行工具交互比较简单,主要有两部分:参数和子命令。可以看到像--confxxx.yaml这样带参数名的是参数,参数名也可以省略,比如converta.jpga.png,其中a.jpg和a.png也是参数.子命令更容易理解。我们日常使用的git用到的子命令很多,比如gitaddxxx.jpg。当子命令可以继续应用子命令时,子命令也有自己的参数。基于命令行的特点,我们完全可以通过YAML来描述命令行工具的使用规范,现在一切都可以是YAML。这里我给出一个阿里云命令行工具的YAML定义,当然只是一个demo。如下:name:aliyun2version:"0.1.0"about:"cliforAlibabaCloud"args:-version:short:vlong:versiontakes_value:falseabout:Displayversionsubcommands:-oss:about:objectstoragesubcommands:-cat:about:cat文本文件args:-file:takes_value:truerequired:trueabout:filename-ls:about:listfile-ecs:about:cloudserversubcommands:-SendFile:about:sendfile-AddTags:about:addtags可以看出我先定义了两个子命令:oss和ecs,然后我在oss子命令下定义了两个子命令:cat和ls。对于oss的cat子命令,我添加了file参数,这样就可以使用cat查看oss上的文本文件内容了。有了这个命令行工具YAML规范定义,我就可以调用clap-rs提供的命令行工具接口生成相应的shell提示脚本。效果如下:这个命令行提示符的效果是不是比原来的好多了?提示有了描述,选择子命令和参数就容易多了。四为所有命令行工具编写YAML说到这里,相信大家都明白了。无论工具是用Java、Python、Node.js还是Rust编写的,首先定义工具的YAML规范,然后开发者根据规范编写代码,他可以选择自己喜欢的语言,自己喜欢的命令行解析器,然后实现相应的功能。如果不改进代码,很难写出没有错误的YAML文件,所以我做了一个JSONSchema[2]文件,它可以在编写YAML文件时提供代码提示,使编写命令行YAML规范文件变得更加容易。JSONSchema的使用方法如下:接下来,我们将基于YAML文件,为各种shell生成对应的命令行提示脚本,如bash、zsh、fish、powershell等,开发者无需再去处理那些other命令行提示不明,或者找命令行代码提示的编程语言对应的SDK。如果不是呢?就算有,如果生成的提示很简单呢?毕竟,命令行工具提示非常重要。我相信Node.js开发者都不想学习Rust和clap-rs,那样效率太低了。所以我又写了一个工具cli-completion[3],它的主要目的是帮你根据上面提到的YAML文件自动生成各种shell的命令行提示脚本。我们来看一个zsh的例子:zsh:$mkdir~/.oh-my-zsh/custom/plugins/aliyun2$cli-completion--zshaliyun2.yaml>~/.oh-my-zsh/custom/plugins/aliyun2/_aliyun2这样,cli-completion可以为任何命令提供命令行提示符。也就是说,以后你只需要写命令行逻辑,所有关于命令行提示的问题都会由cli-completion帮你生成。当然,考虑到用户体验,可能需要通过命令行工具中的子命令将cli-completion生成的脚本快速同步到客户端环境。命令行开发流程:编写YAML规范,自动生成命令行提示符,开发者完成功能实现后下班。有的同学可能会问,我是不是可以基于YAML文件,加上一定的命令行解析框架,自动完成整个应用的骨架生成,完全可以。开发者只需要实现部分功能,开发起来会更简单。我个人认为使用像PicoCli这样的框架来自动生成代码是完全没有问题的。5、把cli-completion转为FaaS的功能,一年可能用不到两次,安装又费时又麻烦。FaaS现在不是无处不在,我们也可以尝试一下。首先,cli-completion是用Rust写的,所以你可以用传统的方式写RustCloudLambda,然后部署到云服务上。另外,你也可以写一个Rustweb应用,比如actix-web,也很简单。这些都不够时尚。我们打算WebAssembly的cli-completion代码并以FaaS模式部署它。这里我选择CloudFlare作为FaaS的运行平台。让我们看一下演示。创建cli.yaml文件如下:name:cli1version:"0.1.0"about:"CLIcompletionforbash,zsh,fishandpowershell."args:-help:short:hlong:helptakes_value:falseabout:Displaythishelp然后调用cli-completion的FaaS服务,即可得到对应的命令行提示脚本代码。命令如下:curl-H'Content-Type:application/x-yaml'--data-binary"@cli.yaml"https://cli-completion.linux-china.workers.dev/completion/zsh与传统的Cloudlambda或cloudfunction相比,这种方式拥有最快的FaaS响应速度,并且调用该服务的次数非常少。基本上每次请求都是冷启动,WebAssembly在这方面很有优势。当然,还有一个最大的原因:就是WebAssembly的FaaS,它是最便宜的。题外话讨论cloudflare的WebAssmebly的实现,纯技术讨论,代码如下:asyncfunctionhandleRequest(request){const{greet}=wasm_bindgenawaitwasm_bindgen(wasm)constgreeting=greet()returnnewResponse(greeting,{status:200})}上面代码中,wasm是一个WebAssembly.Module对象,是外部注入的,不是开发者写的,而是FaaS生成的。接下来就是从wasm_bindgen函数中获取wasmexport函数,然后调用wasm_bindgen(wasm)将greet函数与wasm模块中的export函数关联起来,然后调用greet会转为wasm模块的调用。如果是这种情况,WebAssembly.Module实际上可以在外部进行管理。当有请求时,会关联JavaScript函数,保证WebAssembly的快速响应。6.总结以后写命令行工具的时候,再也不用担心代码提示了。在开始开发工具之前,先写一个YAML文件,整理清楚自己的思路,有哪些子命令,参数等等,然后基于YAML文件进行开发,用什么语言都无所谓,最后配合cli-completion完成命令行提示,你的命令行工具还是相当专业的,至少从表面上看是这样:)最后,列出一些命令行应用中涉及到的关键命令行解析器,供你后续参考:Java:Picocli,JCommander,JOpt,kotlinx-cli、JLine、args4jNode.js:Commander.js、clap.js、minimist、yargs[4]Deno:yargsPython:argparse、docopt、cli-args、clapGolang:argparse、flaggyRust:clap-rs、pico-args,pawRuby:cmdparse,commander,GLIC++:gflags,cli,docopt.cpp
