当前位置: 首页 > 科技观察

从零开始打造个人命令行工具集——yargs完全指南

时间:2023-03-15 00:39:02 科技观察

【引自ideras.me的博客】前言使用命令行程序对于程序员来说是非常普遍的,即使对于前端工程师或者开发gui也是如此。需要使用命令行来编译程序或打包程序。熟练使用命令行工具可以大大提高开发效率。Linux自带的命令行工具非常好用,但是这些工具都是按照一般需求开发的。如果有一些特殊需求,还是需要自己编写脚本来完成批量重命名文件、批量替换文件内容等任务,以提高工作效率。在node.js出来之前,经常使用python开发一些脚本来完成特殊的任务,比如python爬虫。有很多python相关的教程。有兴趣的可以google。得益于node.js的异步io特性,使用node开发io密集型任务非常简单。本文将告诉你如何使用node.js的yargs模块来开发你自己的命令行工具集合。命令行参数解析yargs是一个npm模块,用于完成命令行参数解析。回到使用shell开发命令行的时代,getopts是第一代命令行参数解析工具,通过shell=>python=>node.js其实命令行参数解析程序并没有太大的进化。它们的目的始终是将用户从命令行传入的参数解析成指定的格式供程序使用。虽然没有太大的变化,但是由于开发了一个命令行参数模块,比较简单,所以在node社区有很多类似yargs的开源项目。这是一个简短的列表。有兴趣的可以自己去了解一下,然后选择自己喜欢的项目使用。minimist源自optimist,模仿python的optimist项目commander.jstj是node.js的大师,co的作者,commander.js源自ruby的commander项目,作者也是tj。noptnpm项目使用nomnom,不再维护。不推荐使用yargs看了阮一峰的Node.js命令行程序开发教程,开始使用yargs开发自己的命令行工具。使用一段时间后,我发现它非常好用。自从阮大神的文章发表后,yargs发生了一些变化,增加了很多有用的函数,尤其是函数.commandDir(directory,[opts]),对于创建命令行工具集合非常有用,所以写仍然需要新版本的yargs教程。yargs的用法比较简单。对英文有信心的可以去主页看原版:yargs简单模式yargs默认使用两个--作为参数的前缀,中间可以使用空格或=。下面代码展示了最简单的yargs用法,只需要导入yargs,就可以读取命令行参数,不需要写任何配置,很简单#!/usr/bin/envnodevarargv=require('yargs').argv;if(argv.ships>3&&argv.distance<53.5){console.log('Plundermoriffiwobbles!');}else{console.log('Retreatfromthexupptumblers!');}$./plunder.js--ships=4--distance=22Plundermoriffiwobbles!$./plunder.js--ships12--distance98.7Retreatfromthexuptumblers!示例代码全部来自官网:yargs简单模式也可以读取short变量比如-x4相当于argv.x=4简单模式也可以读取boolean类型-s相当于由于argv.s=true,所以简单模式也可以读取非起始变量。这种类型的变量存储在argv._数组中。只需一行代码就可以实现参数配置简单模式的功能varargv=require('yargs').argv;但是如果你想统计变量出现的次数怎么办?答案是添加参数配置选项。#!/usr/bin/envnodevarargv=require('yargs').count('verbose').alias('v','verbose').argv;VERBOSE_LEVEL=argv.verbose;functionWARN(){VERBOSE_LEVEL>=0&&控制台.log.apply(console,arguments);}functionINFO(){VERBOSE_LEVEL>=1&&console.log.apply(console,arguments);}functionDEBUG(){VERBOSE_LEVEL>=2&&console.log.apply(console,arguments);}WARN("Showingonlyimportantsstuff");INFO("Showingsemi-importantsstufftoo");DEBUG("Extrachattymode");上面的程序可以统计verbose参数出现的次数,缩写-v也会统计。Forspecificcallingexamples,refertothefollowingcode$nodecount.jsShowingonlyimportantstuff$nodecount.js-vShowingonlyimportantstuffShowingsemi-importantstufftoo$nodecount.js-vvShowingonlyimportantstuffShowingsemi-importantstufftooExtrachattymode$nodecount.js-v--verboseShowingonlyimportantstuffShowingsemi-importantstufftooExtrachattymodeyargs提供很多接口用来帮助完善命令行程序,提示使用方法varargv=require('yargs').usage('Usage:$0-w[num]-h[num]').argv;Requiredparameters#!/usr/bin/envnodevarargv=require('yargs')。用法('用法:$0-w[num]-h[num]').demand(['w','h']).argv;提供参数默认值#!/usr/bin/envnodevarargv=require('yargs').default('x',10).default('y',10).argv;控制台日志(argv.x+argv.y);打印帮助信息#!/usr/bin/envnodevarargv=require('yargs').usage('Usage:$0[options]').help('h').alias('h','help').epilog('copyright2015').argv;使用别名varargv=require('yargs').usage('Usage:$0[options]').alias('h','help').argv;访问argv.h相当于访问argv.help参数数组varargv=require('yargs').usage('Usage:$0[options]').alias('n','name').array('n').argv;console.log(argv.n);调用nodearray_test.js-nabctest设置参数范围varargv=require('yargs').alias('i','ingredient').describe('i','chooseyoursandwichingredients').choices('i',['peanut-butter','jelly','banana','pickles']).help('help').argv上面代码设置了argv.i的值只能是['peanut-butter','jelly','Banana','pickles']以上其中一个是yargs比较简单的用法,如果想阅读完整版,建议阅读github上的subcommandsyargs的另一个原因适合开发复杂命令行程序的地方在于它支持子命令,而且子命令可以嵌套,也就是说你也可以像git那样用上百条命令开发一个程序。yargs子命令有两种模式:.command(*)和.commandDir(directory,[opts]).command.command方法有三个接口.command(cmd,desc,[builder],[handler]).command(cmd,desc,[module]).command(module)其实它们的用法是差不多的。您可以将它们视为将模块传递给yargs。这个模块必须导出四个变量cmd、desc[builder]、[handler],其中builder和handler是方法,另外两个是字符。使用第一个接口的字符串示例argv){console.log(argv.url)}).help().argv需要把这个模块放在一个单独的文件中才能使用第三个接口,然后使用require导入这个模块的代码//my-module.jsexports.command='get[proxy]'exports.describe='makeagetHTTPrequest'exports.builder={banana:{default:'cool'},batman:{default:'sad'}}exports.handler=function(argv){//dosomethingwithargv.}导入时,使用yargs.command(require('my-module')).help().argv当附加模块没有定义cmd和desc时,第二个接口可以使用yargs.command('get[proxy]','makeagetHTTPrequest',require('my-module')).help().argv这里推荐使用第三个接口,可以保持模块的内聚性,您可以在任何命令下安装此模块。迁移时不需要修改模块代码。只需要修改导入模块的代码即可实现.commandDir如果有大量的命令是使用上面的.command(module)开发的,这些模块具有相同的结构,应该有一种方法可以简化这些命令的导入过程,并使这个过程自动化。为此,yargs提供了.下面的commandDir接口参考了自己写的一个项目坑。下面是这个项目的目录结构。├──client.js││├──login.js││├──scope.js││├──scope.json│├──secret.json││├──token.json││└──upload.js│├──gg.js│├──git││├──commit.js││├──create.js││├──deploy.js│├├──push.js││└──token.json│├──git.js│├──gm.js│├──md5.js│├──新闻││├──bing.js│├──funs.js││├──funs.json││├──games.js││├──games.json││├──google.js││├──newsall.json│├──shops.js││├──shops.json││├──videos.js││└──videos.json│└──news.js└──pit.jspit.js:命令行入口#!/usr/bin/envnoderequire('yargs').commandDir('pit').demand(1).help().locale('en').showHelpOnFail(true,'Specify--helpforavailableoptions').argv···这段代码只指定读取命令同目录下的同名文件夹`pit`下作为子命令加载>**注意**:commandDir默认只会加载目录***级的文件,不会递归加载。如果要递归加载,需要这样写`.commandDir('pit',{recurse:true})`接下来我们看git的子命令,因为git项目每次都要重复同样的步骤已提交,所以我想开发一个更简单的命令来打包提交git.jsexports.command='git';exports.desc='githubcommandlist';exports.builder=function(yargs){returnyargs.commandDir('git')}exports.handler=function(argv){}git也加载一个目录作为它自己的目录子命令:以commit为例function_exec(cmd){vardeferred=Q.defer();exec(cmd,function(code,stdout,stderr){deferred.resolve();});returndeferred.promise;}exports.command='commit';出口。desc='commitrepolocal';exports.builder=function(yargs){returnyargs.help('h');};exports.handler=function(argv){varrepo=process.cwd();varname=path.basename(repo);Q.fcall(function(){}).then(()=>_exec(gitadd.)).then(()=>_exec(gitcommit-m'd')).catch(function(err){console.log(err);}).done(()=>{console.log(commit${repo}done);});}```这条命令默认运行在git项目的根目录下,和git命令不一样,git可以运行在项目根目录下的任意子目录,使用shelljs运行子命令,然后使用Q进行promise封装,保证命令的执行顺序,同时打印命令行输出并将错误消息发送给控件。一个非常简单省时的命令行程序,用作抛砖引玉。高手善于使用命令行(电影中的高手也是如此)。当你习惯了使用命令行来完成日常任务时,你就会逐渐形成一种依赖。继续下去,你会考虑使用命令行来完成一切。当然,这个目标是无法实现的,因为能够自动完成所有任务的命令行不叫命令行——它叫AI。虽然你无法开发出高度智能的人工智能,但仍有许多任务可以使用命令行来完成。在这里我写下我的想法,供大家参考。有了这些api接口,就可以完全实现像aws、googlecloud、aliyun这样的云主机了。使用命令行可以节省大量的运维时间。另外也可以参考上面pit.js写的douban.js来抓取豆瓣。数据,豆瓣的公共API可以免认证访问,对于一些测试来说非常方便。命令行爬虫使用node.js开发爬虫和使用python一样简单,但是一个功能齐全的爬虫必须要有命令行界面。可能每次有新需求修改代码,下次给大家分享一个简单的基于node.js的爬虫项目表单提交。对于一些不提供API接口但想使用命令交互的网站,可以使用表单提交登录,然后做一些登录后才能做的事情:比如发布文章,现在很多网站都支持使用markdown编辑文章然后发布。对于这类网站,可以开发自己的命令行unity进行管理,当你写完一篇文章后,只需要一个简单的命令,就可以将文章同时推送到各大网站