开始下面操作前请确保node环境已经正确安装,并配置了全局环境变量,代码在windows系统下编写未做系统适配。

初始化项目

npm init -y 初始化package.json

配置 package.json

新增"bin"字段,设置快捷命令和入口文件

{"name": "fn-cli","version": "1.0.0","description": "","bin": {"fn": "./bin/index.js","fn-cli": "./bin/index2.js",},// "bin": "/bin/index.js", 以name为key"main": "index.js","scripts": {"test": "echo \"Error: no test specified\" && exit 1"},"keywords": [],"author": "","license": "ISC"
}

bin可以直接写入口文件的地址,此时快捷命令的名称为name字段的值

bin设置为对象的形式,可以设置多个快捷命令。

新增入口文件

这里配置的bin的入口文件是bin/index.js 所以直接在根目录下新增bin文件夹并在目录下新增index.js

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6Nf3MgLM-1646989638186)(https://secure2.wostatic.cn/static/qFkxqbeAjWewueo9cGrS7h/image.png)]

为了执行命令时,可以直接运行对应的js文件脚本,在index.js 入门文件头部添加:

#!/usr/bin/env node 指定该文件是使用系统环境的node环境执行该脚本

添加内容:

#!/usr/bin/env node
console.log('TEST')

配置命令软链接

在根目录下执行 npm link

没有错误日志输出,并且提示 up to date 就表示建立命令软连接成功

直接控制台输入:对应的命令名称就可以执行,输入fn-cli 就可以看到结果TEST输出

一个最简单的CLI就成功了。

如果你修改了 bin字段的,需要重新建立软链接,建议到node根目录下删除之前建立的软链接文件

扩展CLI

此时的cli只能简单的执行但一个命令,实现功能单一无法实现丰富的功能。

vue-cli 可以使用 vue create demo 来新建一个项目, vue -V 可以查看当前cli 版本等等

命令参数解析

当前的CLI不管我们在fn-cli后面加上一些什么参数,都是执行同样的操作。

要让CLI具备支持多命令多功能,第一步就是拿到用户输入的参数

修改一下index.js

#!/usr/bin/env node
console.log('fn-cli', process.argv);

执行命令:

可以看出process.argv 是一个数组类型变量,默认长度为2,分别为node环境执行文件位置和当前命令行入口文件位置。从第3个参数就是用户输入的命名

当我们在命令后面在跟上一下其他参数是可以发现,process.argv 解析参数是以空格为分割的。

定义CLI规范

目前知道了CLI如何去解析参数,那么要扩展CLI ,首先得定义规范:

process.argv 的第三个参数为:命令名称,从第四个参数开始为:命令的参数

命名参数:

支持—debug=true —name=demo12 来指定参数的键值。

默认命名后面的普通字符串为默认参数值

#!/usr/bin/env node
const argvs = process.argv.slice(2);
// 规范:
// 第三个参数为命名名称,从第四参数开始为命令的参数 (这里的位置以 process.argv为基准)
const command = argvs[0];
const params = argvs.slice(1);console.log('fn-cli', command, params);

开始编写命令

统一参数

在上一步中,可以发现参数解析出来是字符串,存在—name=demo12 这种字符形式代表参数键值对,这里需要首先对这种参数类型进行解析,统一成特定的参数类型。

下面为:参数解析方法这个defaultValue参与覆盖操作,普通字符靠后覆盖前面字符串作为默认值;

/*** @description: 对参数键值对进行解析,普通字符串作为defaultValue* @param {*}* @return {*}*/
const paramsParse = () => {const config = params.reduce((pre, val) => {const reg = /^\-\-/gm;if (val.indexOf('=') === -1) return {...pre,defaultValue: val};if (reg.test(val)) {const param = val.replace(reg, '').split('=');} else {pre[param[0]] = param[1];}pre[param[0]] = param[1];return pre;}, {})return config;
}

第一个命令

为了方便扩展命令,这里先建立 comand文件夹 并新增一个index.js 作为 command整合入口文件。

bin/command/index.js

const requireContext = require('node-require-context')
const commands = requireContext('./', false, /\.js$/);
const commanders = {};
commands.keys().forEach(moduleId => {const moduleName = moduleId.replace(/(\.\/|\.js)/g, '').split('command')[1].slice(1);if(moduleName === 'index') return;const module = require(`./${moduleName}`);commanders[moduleName] = module;
})module.exports = commanders;

bin/command/version.js

const path = require('path');
const pkgPath = path.join(__dirname, '../../package.json');
const pkg = require(pkgPath);
function version() {console.log(pkg.version);
}
module.exports = version;

bin/``command/create.js

const path = require('path');
const fs = require('fs');function create(params) {const fileNameReg = /^.+\.\w{1,}/;const name = params.name || params.defaultValue;const dirPath = path.join(process.cwd(), name);if(!fileNameReg.test(name)) {if (fs.existsSync(dirPath) === false) {fs.mkdirSync(dirPath);console.log(`${name}: 文件夹创建成功!`,)} else {console.log(`${name}: 文件夹已存在!`,)}} else {try {fs.readFileSync(dirPath, 'utf-8');console.log(`${name}: 文件已存在!`,)}catch(e) {fs.writeFileSync(dirPath, '', 'utf8');console.log(`${name}: 文件创建成功!`,)}}
}module.exports = create;

bin/index.js

#!/usr/bin/env node
const path = require('path');
const fs = require('fs');
const commanders = require('./command/index');
const argvs = process.argv.slice(2);
// 规范:
// 第三个参数为命名名称,从第四参数开始为命令的参数 (这里的位置以 process.argv为基准)
const command = argvs[0];
const params = argvs.slice(1);const paramsParse = () => {const config = params.reduce((pre, val) => {const reg = /^\-\-/gm;if (val.indexOf('=') === -1) return {...pre,defaultValue: val};if (reg.test(val)) {const param = val.replace(reg, '').split('=');} else {const param = val.split('=');}pre[param[0]] = param[1];return pre;}, {})return config;
}if (!command) {const commanderKeys = Object.keys(commanders).join('、');console.log(`Usage: fn <command> [options] \n command support: ${commanderKeys}`);
} else {const commanderParams = paramsParse(params)if (!commanders[command]) {console.log('不支持当前命令');} else {commanders[command](commanderParams);}
}

Yargs快速建立CLI

使用yargs 可以快速建立一个cli的基本框架:自动帮你解析参数和命令

#!/usr/bin/env node
const yargs = require('yargs');
const { hideBin } = require('yargs/helpers');
const pkg = require('../package.json');
const args = hideBin(process.argv);
const argv = process.argv.slice(2);
const context = {version: pkg.version,
}
const cli = yargs()
cli.usage('Usage: practice-cli [command] <options>') // 用法.demandCommand(1, 'A command is required.Pass --help to see all avaiable commands') // 无command提示.recommendCommands().fail((err, msg) => {console.log('err', err, msg);}).strict() // 严格模式.alias('h', 'help').alias('V', 'version') // 别名.wrap(cli.terminalWidth()) // 实现文字两侧顶格显示.epilogue('Pratice CLI').options({debug: {type: 'boolean',describe: 'Bootstrap debug mode',alias: 'd',}}).group(['debug'], 'Dev Options').command('init [name]', 'Do init a project', (yargs) => {yargs.option('name', {type: 'string',describe: 'Name of project',alias: 'n'})}, (argv) => {console.log('argv', argv);}).parse(argv, context);

从零开始的命令行CLI相关推荐

  1. python 基础命令-Python 命令行(CLI)基础库

    在 CLI 下写 UI 应用 前阵子看了一下自己去年写的 Python-视频转字符动画,感觉好糗..所以几乎把整篇文章重写了一遍.并使用 curses 库实现字符动画的播放. 但是感觉,curses ...

  2. windows 中开机自启动命令行(CLI)

    windows 中开机自启动命令行(CLI) English-Version 1. 创建自启动批处理文件 新建一个.txt文档,在里面输入代码 @echo off CMD 然后把文件更名为start. ...

  3. jenkins 命令行 CLI jenkins-cli.jar

    部署好jenkins后,一般都是通过jenkins提供的web界面来操作jenkins. 而有些场景则需要通过命令来操作jenkins,例如通过脚本操作jenkins. 在jenkins提供的web界 ...

  4. .NET Core系列 : 1、.NET Core 环境搭建和命令行CLI入门

    2016年6月27日.NET Core & ASP.NET Core 1.0在Redhat峰会上正式发布,社区里涌现了很多文章,我也计划写个系列文章,原因是.NET Core的入门门槛相当高, ...

  5. php think命令,ThinkPHP 使用命令行 (cli) think 调用

    在某些场景里,我们需要在命令行中调用代码首先,在applicationcommand目录(目录没有则创建)下新建hello.php:<?php namespace app\command; us ...

  6. vscode/typora+picGo-core(命令行CLI)/picGo(GUI)+图片上传(github/smms)/批量上传/typora语法扩展渲染功能设置/修改本地图片存放位置配置

    文章目录 typora&vscode 性能对比(渲染速度) picgo_GUI中文环境 GUI data.json 总体配置文件 命令行任意环境 安装picgo-commandLine 配置文 ...

  7. 拉取网页_用命令行管理你的 GitHub 项目,不必再开网页,官方 CLI 工具 1.0 版上线...

    (给Linux爱好者加星标,提升Linux技能) 转自:量子位 经过大半年的迭代与完善,GitHub CLI已经成功从beta版升级为1.0正式版,可供所有用户使用. GitHub CLI让开发者在终 ...

  8. hive退出命令_从零开始学习大数据系列(五十五)使用Hive命令行及内置服务

    [本文2000字左右,预计阅读需要15-20分钟] 让我们来回顾下之前用到的Hive用户的交互接口.Hive用户接口主要有三个:命令行(CLI),客户端(Client)和Web界面(WUI).$HIV ...

  9. PHP 命令行模式实战之cli+mysql 模拟队列批量发送邮件(在Linux环境下PHP 异步执行脚本发送事件通知消息实际案例)...

    源码地址:https://github.com/Tinywan/PHP_Experience 测试环境配置: 环境:Windows 7系统 .PHP7.0.Apache服务器 PHP框架:ThinkP ...

最新文章

  1. coco creator编辑动画坑之拖图片
  2. 【转载】单片机应用系统断电时的数据保护方法
  3. python操作mysql数据库(增、删、改、查)_python对 MySQL 数据库进行增删改查的脚本...
  4. ElasticSearch 6.4.3 启动报错: [Cannot assign requested address: bind]
  5. Oracle11gr2新增APPEND_VALUES提示
  6. Nginx高可用实战
  7. 老程序员为什么从不使用 Java 自带的序列化?
  8. 机器学习基础(二)——训练集和测试集的划分
  9. 阿里研究员玄难:如何做电商业务中台
  10. 参加2010年磨房《在路上 - 十年》百公里徒步活动小记
  11. 上市公司环境数据集:环境绩效明细表、排放明细表、资源消耗明细表等多项指标数据
  12. expr4j 表达式计算
  13. 吴恩达OpenAI最新课程:prompt-engineering-for-developers读书笔记
  14. 高数_第5章常微分方程__一阶微分方程
  15. Scratch学习有什么优点
  16. 国内TeamViewer正版授权的代理商有哪些?如何查询?
  17. 《经济学通识课》读书笔记
  18. IOS中impactor报182错误解决方法
  19. html电气自动化专业学吗,电气工程及其自动化难学吗 适合哪些人学
  20. C++11 使用中结者模式实现ChatRoom逻辑

热门文章

  1. Word7中尾部空格不能显示下划线的解决方法
  2. SpringSecurity以及Oauth2(笔记)
  3. 微信小程序图片轮播+预览效果实现
  4. MATLAB 冒泡算法
  5. Lvs+keepAlived实现负载均衡高可用集群(DR实现)
  6. 4.2nbsp;反身理论与均衡价格论
  7. Mysql上线长时间以后重新发起请求报错
  8. PyTorch深度学习入门 || 系列(四)——非线性回归
  9. C# #region简单使用
  10. requests实现登陆、点赞、批量点赞、取消点赞