命令行工具开发(三)使用commander完成命令和选项的解析

282 阅读7分钟

Hello 大家好,这个是系列教程命令行工具开发的第三篇文章命令行工具开发使用commander完成命令和选项的解析

上文说了命令行工具开发命令和选项解析,今天说下命令行工具开发中的使用commander完成命令和选项的解析

“前文传送门,

命令行工具开发(一)入门

命令行工具开发(二)命令和选项解析

命令行工具开发使用commander完成命令和选项的解析

在命令行工具中,我们经常需要处理多个命令,比如 npm installnpm runnpm test 等。这些命令都是 npm 的子命令,我们可以通过 npm 命令来执行不同的操作。

1. 准备工作

创建项目

mkdir helloworld-cli
cd helloworld-cli
npm init -y

创建入口文件

cd helloworld-cli
touch index.js

index.js 文件内容

#!/usr/bin/env node
console.log('Hello, World!')

目录结构

helloworld-cli
├── index.js
└── package.json

在 package.json 中添加可执行文件

package.json中添加一个bin字段,用于指定可执行文件的位置,比如:

{
  "name": "helloworld-cli",
  "version": "1.0.0",
  "description": "CLI to test command line arguments",
  "main": "index.js",
  "bin": {
    "helloworld-cli": "index.js"
  }
}

将命令行工具添加到全局

cd helloworld-cli
npm link

不知道上面这些操作是在做什么?去看看前文呗,传送门 >> 命令行工具开发之入门(一)

2. 命令和选项解析

2.1 设计我们的命令行工具

假设,我们希望我们的命令行工具包含以下命令:

  • help 列出所有可用的命令及其命令的描述
  • random 生成一个随机数
  • time 输出当前时间
  • hello 输出 "Hello, World!"
  • weather 输出 "某些城市的天气"

这些命令都是我们命令行工具的子命令,我们可以通过 helloworld-cli <command> 的形式来执行这些命令。

在实现这些命令之前,先介绍下这篇文章的主角commander这个 npm包是做什么的:commander 是一个用于解析命令行参数的库,我们可以使用它来解析命令行参数,并根据参数来执行不同的操作。

老规矩,要用这个包,当然是要先安装commander

cd helloworld-cli
yarn add commander
# or
npm install commander

2.2 初始化一个commander实例

#!/usr/bin/env node
const { Command } = require('commander');
// 创建一个命令行工具实例
const program = new Command();
program
    // 定义 cli工具的名称和描述
    .name('helloworld-cli')
    // 定义 cli工具的描述
    .description('CLI to test command line arguments')
    // 定义 cli工具的版本
    .version('0.8.0')
// 解析命令行参数
program.parse();

这时候helloworld-cli是没有任何命令的,但是可以使用 helloworld-cli --version 来查看我们的命令行工具的版本。

cd helloworld-cli
helloworld-cli --version

version.png

还可以使用 helloworld-cli --help 来查看我们的命令行工具的帮助信息。

cd helloworld-cli
helloworld-cli --help

help.png

2.3 分别实现randomtimehelloweather命令

2.3.1 实现random命令

#!/usr/bin/env node
const { Command } = require('commander');
// 创建一个命令行工具实例
const program = new Command();
program
    // 定义 cli工具的名称和描述
    .name('helloworld-cli')
    // 定义 cli工具的描述
    .description('CLI to test command line arguments')
    // 定义 cli工具的版本
    .version('0.8.0')
// 定义random命令
program.command('random')
    // 定义命令的描述
    .description('生成一个随机数')
    // 定义命令的处理函数
    .action(() => {
        console.log(Math.floor(Math.random() * 10))
    });

// 解析命令行参数
program.parse();

运行下random命令

cd helloworld-cli
helloworld-cli random

random.png

2.3.2 实现time命令

如果单纯输出当前时间,那么time命令的实现就跟random一样简单了,但是,我们希望time命令可以接受一个参数,用于指定时间的格式,比如:

helloworld-cli time -f YYYY-MM-DD HH:mm:ss

怎么给一个命令添加选项呢?

commander 提供了 option 方法,用于给命令添加选项。

option 方法接受三个参数,第一个参数是选项的名称,第二个参数是选项的描述,第三个参数是选项的默认值。

#!/usr/bin/env node
const { Command } = require('commander');
// 引入dayjs库,用于格式化时间
const dayjs = require('dayjs'); 
// 创建一个命令行工具实例
const program = new Command();
program
    // 定义 cli工具的名称和描述
    .name('helloworld-cli')
    // 定义 cli工具的描述
    .description('CLI to test command line arguments')
    // 定义 cli工具的版本
    .version('0.8.0')
// 定义random命令
program.command('random')
    // 定义命令的描述
    .description('生成一个随机数')
    // 定义命令的处理函数
    .action(() => {
        console.log(Math.floor(Math.random() * 10))
    });
// 定义time命令
program.command('time')
    // 定义命令的描述
    .description('输出当前时间')
    // 定义命令的选项 -f YYYY-MM-DD 或者 --format YYYY-MM-DD 都可以
    // --format 其实就是 -f 的别名啦
    .option('-f, --format <format>', '时间格式', 'YYYY-MM-DD HH:mm:ss')
    .action((options) => {
        // 当命令无参数时,options为 action 的函数的第一个参数
        const { format } = options;
        const str = dayjs().format(format);
        console.log(str);
    });
// 解析命令行参数
program.parse();

运行下time命令

cd helloworld-cli
helloworld-cli time
helloworld-cli time -f YYYY-MM-DD
helloworld-cli time --format YYYY-MM-DD

time.png

2.3.3 实现hello命令

hello命令着实简单,要不我们让它接收下用户输入的姓名name, 如果输入了姓名就输出hello ${name},否则输出hello world

helloworld-cli hello [name]

怎么给一个命令添加参数呢? commander 提供了 argument 方法,用于给命令添加参数。

argument 方法接受两个参数,第一个参数是参数的名称,第二个参数是参数的描述。

上代码!!!

注意哈,argument 方法必须在 action 方法之前调用。

#!/usr/bin/env node
const { Command } = require('commander');
// 引入dayjs库,用于格式化时间
const dayjs = require('dayjs');
// 创建一个命令行工具实例
const program = new Command();
program
    // 定义 cli工具的名称和描述
    .name('helloworld-cli')
    // 定义 cli工具的描述
    .description('CLI to test command line arguments')
    // 定义 cli工具的版本
    .version('0.8.0')
// 定义random命令
program.command('random')
    // 定义命令的描述
    .description('生成一个随机数')
    // 定义命令的处理函数
    .action(() => {
        console.log(Math.floor(Math.random() * 10))
    });
program.command('time')
    .description('输出当前时间')
    .option('-f, --format <format>', '时间格式', 'YYYY-MM-DD HH:mm:ss')
    .action((options) => {
        // 当命令无参数时,options为 action 的函数的第一个参数
        const { format } = options;
        const str = dayjs().format(format);
        console.log(str);
    });
program.command('hello')
    .description('输出 "Hello, World!"')
    .argument('[name]', '你的名字')
    .action((name) => {
        const greeting = name ? `Hello, ${name}!` : 'Hello, World!';
        console.log(greeting);
    });
// 解析命令行参数
program.parse();

运行下hello命令

helloworld-cli hello
helloworld-cli hello zhangsan

hello.png

2.3.4 实现weather命令

weather命令,用于查询指定城市的天气情况。 weather命令需要接收一个参数,一个是城市名称(city),然后可以使用-d选项来指定要查询几天内的天气。如果用户没有指定日期,则默认查询今天的天气情况。

helloworld-cli weather [city] -d [days]
const { Command } = require('commander');
// 引入dayjs库,用于格式化时间
const dayjs = require('dayjs');
// 创建一个命令行工具实例
const program = new Command();
program
    // 定义 cli工具的名称和描述
    .name('helloworld-cli')
    // 定义 cli工具的描述
    .description('CLI to test command line arguments')
    // 定义 cli工具的版本
    .version('0.8.0')
// 定义random命令
program.command('random')
    // 定义命令的描述
    .description('生成一个随机数')
    // 定义命令的处理函数
    .action(() => {
        console.log(Math.floor(Math.random() * 10))
    });
program.command('time')
    .description('输出当前时间')
    // .argument('[time]', '指定时间')
    .option('-f, --format <format>', '时间格式', 'YYYY-MM-DD HH:mm:ss')
    .action((options) => {
        // 当命令无参数时,options为 action 的函数的第一个参数
        const { format } = options;
        const str = dayjs().format(format);
        console.log(str);
    });
program.command('hello')
    .description('输出 "Hello, World!"')
    // .argument('<name>', '你的名字') // 必填参数
    .argument('[name]', '你的名字')
    .action((name) => {
        const greeting = name ? `Hello, ${name}!` : 'Hello, World!';
        console.log(greeting);
    });
program.command('weather')
    .description('输出城市天气')
    // 定义命令的参数 city
    .argument('[city]', 'city to search')
    // 定义命令的选项 days
    .option('-d, --days <day>', '几天内的天气', '1')
    .action((city, options) => {
        console.log(city);
        console.log(options);
        // 获取天数选项
        const { days } = options;
        // 循环输出天气
        for (let index = 1; index <= days; index++) {
            console.log(`Day ${index}: ${city} 的天气`);
        }
    });
// 解析命令行参数
program.parse();

运行下weather命令

helloworld-cli weather 上海 -d 2

weather-d.png

等等,我们似乎漏了个东西,就是help命令,好像没有实现。其实啊,当我们一个一个添加命令时,commander已经帮我们实现了help命令。

helloworld-cli help

help-all.png

3. 进阶用法

3.1 默认命令

当用户在命令行中输入helloworld-cli时,会执行默认命令。默认命令可以通过isDefault选项来指定。

program.command('glowing-terms', { isDefault: true })
    .description('彩虹屁命令🌈💨')
    .action(() => {
        // 彩虹屁数组
        const caiHongPi = [
            '天涯何处无芳草,你是鲜花不是草。',
            '我做事十拿九稳,现在只差你一吻。',
            '月亮很亮,亮也没用,没用也亮,我喜欢你,喜欢也没用,没用也喜欢',
            '谁的童话书又没合好让公主跑出来了',
            `你笑起来真好看,像春天的花一样`,
            '为什么我脸皮那么厚还是包不住对你的喜欢,一不小心就露馅了',
            '你的眼里有星星✨我的眼里都是你',
            '如果你是五彩的糖,那我就当保护你小小的糖纸',
            '想和你说,今天的云和你,都十分可爱',
            '我真的好喜欢你啊 第一句话是假的 第二句也是。对方申请做您心尖尖上的宝贝,接受请求吗?'
        ]
        // 生成随机数
        const randomIndex = Math.floor(Math.random() * 10);
        // 随机输出彩虹屁
        console.log(caiHongPi[randomIndex]);
    });

glowing-terms.png

3.2 参数必填

program.command('hello')
program.argument('<name>', '你的名字') // 必填参数

3.3 参数可选

program.command('hello')
program.argument('[name]', '你的名字') // 必填参数

3.4 命令增加布尔选项

// vite --open
program.command('vite')
    .description('启动vite开发服务器')
    // 当option 无参数时,默认为boolean类型
    .option('-o, --open', '是否打开浏览器')
    .action((options) => {
        console.log(options);
        const {
            open
        } = options;
        if (open) {
            console.log('打开浏览器');
        }
    })

3.5 给选项增加参数

.option('-c <city>', '指定要查询的城市')

3.6 给选项增加默认值

.option('-c <city>', '指定要查询的城市', '北京')

3.7 给选项增加参数,参数可选

.option('-c [city]', '指定要查询的城市')

3.8 程序的版本号、名称、描述动态获取

const { Command } = require('commander');
const package = require('./package.json');
program
    .name(package.name)
    .description(package.description)
    .version(package.version);
program.parse();

完整代码

#!/usr/bin/env node
const { Command } = require('commander');
const dayjs = require('dayjs');
const package = require('./package.json');
const program = new Command();

console.log(package.name, package.version);

program
    .name('helloworld-cli')
    .description('CLI to test command line arguments')
    .version('0.8.0')
// 获取 package.json 中的版本号及名称
program
    .name(package.name)
    .description(package.description)
    .version(package.version);

program.command('random')
    .description('生成一个随机数')
    .action(() => {
        console.log(Math.floor(Math.random() * 10))
    });

program.command('time')
    .description('输出当前时间')
    // .argument('[time]', '指定时间')
    .option('-f, --format <format>', '时间格式', 'YYYY-MM-DD HH:mm:ss')
    .action((options) => {
        // 当命令无参数时,options为 action 的函数的第一个参数
        const { format } = options;
        const str = dayjs().format(format);
        console.log(str);
        console.log(options);
    });

program.command('hello')
    .description('输出 "Hello, World!"')
    // .argument('<name>', '你的名字') // 必填参数
    .argument('[name]', '你的名字')
    .action((name) => {
        const greeting = name ? `Hello, ${name}!` : 'Hello, World!';
        console.log(greeting);
    });

program.command('weather')
    .description('输出城市天气')
    .argument('<city>', 'city to search')
    .option('--city', 'city to search')
    .option('-d, --days <day>', '几天内的天气', '1')
    .action((city, options) => {
        console.log(city);
        console.log(options);
    });

program.command('glowing-terms', { isDefault: true })
    .description('彩虹屁命令🌈💨')
    .action(() => {
        // 彩虹屁数组
        const caiHongPi = [
            '天涯何处无芳草,你是鲜花不是草。',
            '我做事十拿九稳,现在只差你一吻。',
            '月亮很亮,亮也没用,没用也亮,我喜欢你,喜欢也没用,没用也喜欢',
            '谁的童话书又没合好让公主跑出来了',
            `你笑起来真好看,像春天的花一样`,
            '为什么我脸皮那么厚还是包不住对你的喜欢,一不小心就露馅了',
            '你的眼里有星星✨我的眼里都是你',
            '如果你是五彩的糖,那我就当保护你小小的糖纸',
            '想和你说,今天的云和你,都十分可爱',
            '我真的好喜欢你啊 第一句话是假的 第二句也是。对方申请做您心尖尖上的宝贝,接受请求吗?'
        ]
        // 生成随机数
        const randomIndex = Math.floor(Math.random() * 10);
        // 随机输出彩虹屁
        console.log(caiHongPi[randomIndex]);
    });

program.parse();

讲完收工~

总结一下:本篇我们开发了一个helloworld-cli命令行工具,并且学习了如何用commander 来解析命令和参数的,也通过解析命令和参数给helloworld-cli工具开发了hellorandomtimeweatherhelp命令,并且给weather命令增加了-c选项,用来指定查询的城市。

欢迎关注我的个人公众号「「小枫学幽默」」一起成长,一起分享生活!!