Skip to content

命令行 CLI

使用 Node.js 构建命令行工具(CLI)是一项常见需求。Node.js 天然适合开发 CLI 工具,原因包括其事件驱动架构和广泛的包支持(如 yargs, commander 等)。

1. 基本 CLI 工具开发

1.1 使用 process.argv 解析命令行参数

process.argv 是一个数组,包含传递给脚本的命令行参数。

javascript
#!/usr/bin/env node

// 获取命令行参数
const args = process.argv.slice(2)
console.log('Arguments:', args)

当使用以下命令调用 Node.js 应用程序时,可以传入任意数量的参数

sh
node cli.js joe name=joe
#  [ 'hello', 'world' ]

说明:

  • process.argv[0] 是 Node.js 的路径。
  • process.argv[1] 是脚本文件路径。
  • process.argv[2] 开始是用户传递的参数。

1.2 解析参数并实现功能

通过 process.argv 拿到到的参数还需要进步进行解析,

sh
pnpm install -D minimist
sh
npm install -D minimist
sh
yarn add -D minimist
sh
bun install -D minimist
js
import minimist from 'minimist'

const argv = minimist(process.argv.slice(2))
console.log(argv)

参数名称之前使用双破折号

sh
node cli.js --name=joe

2. 增强 CLI 的功能

处理命令行参数手动解析可能比较繁琐,可以使用专门的库。

2.1 使用 commander

安装 commander

sh
pnpm install -D commander
sh
npm install -D commander
sh
yarn add -D commander
sh
bun install -D commander

创建一个 CLI 脚本:

javascript
#!/usr/bin/env node

const { Command } = require('commander')
const program = new Command()

program
  .name('my-cli')
  .description('A simple CLI tool')
  .version('1.0.0')

program
  .command('add <num1> <num2>')
  .description('Add two numbers')
  .action((num1, num2) => {
    const result = Number.parseFloat(num1) + Number.parseFloat(num2)
    console.log(`Result: ${result}`)
  })

program
  .command('subtract <num1> <num2>')
  .description('Subtract two numbers')
  .action((num1, num2) => {
    const result = Number.parseFloat(num1) - Number.parseFloat(num2)
    console.log(`Result: ${result}`)
  })

program.parse(process.argv)

运行示例:

bash
# 查看帮助
$ node cli.js --help

# 执行加法
$ node cli.js add 5 3
Result: 8

2.2 使用 yargs

yargs 提供更友好的参数解析。

bash
# 安装 yargs
npm install yargs

创建 CLI 脚本:

javascript
#!/usr/bin/env node

const { hideBin } = require('yargs/helpers')
const yargs = require('yargs/yargs')

yargs(hideBin(process.argv))
  .command(
    'add <num1> <num2>',
    'Add two numbers',
    (yargs) => {
      yargs.positional('num1', {
        describe: 'First number',
        type: 'number',
      })
      yargs.positional('num2', {
        describe: 'Second number',
        type: 'number',
      })
    },
    (argv) => {
      console.log(`Result: ${argv.num1 + argv.num2}`)
    }
  )
  .command(
    'subtract <num1> <num2>',
    'Subtract two numbers',
    (yargs) => {
      yargs.positional('num1', {
        describe: 'First number',
        type: 'number',
      })
      yargs.positional('num2', {
        describe: 'Second number',
        type: 'number',
      })
    },
    (argv) => {
      console.log(`Result: ${argv.num1 - argv.num2}`)
    }
  )
  .help()
  .argv

运行示例:

bash
# 查看帮助
$ node my-cli.js --help

# 执行加法
$ node my-cli.js add 5 7
Result: 12

2.3 命令行用户界面

安装 inquirer 用户交互界面收集用户选择信息

sh
pnpm add -D inquirer @types/inquirer

修改cli.js

js
#!/usr/bin/env node

import commander from 'commander'
import inquirer from 'inquirer'

const PROMPTS = [
  {
    name: 'preprocessor',
    message: 'Select css preprocessor',
    type: 'list',
    choices: ['Less', 'Sass'],
  },
]

program
  .arguments('<file>')
  .option('-u, --username <username>', 'The user to authenticate as')
  .option('-p, --password <password>', 'The user\'s password')
  .action(async (file) => {
    const inputs = await inquirer.prompt(PROMPTS)
    console.log('inputs: ', inputs)
  })
  .parse(process.argv)

快速测试

sh
$ node cli.js
username: name
password: ***
user: name pass: 123 file: cli.js

3. CLI 脚本的发布

3.1 在 package.json 中定义 CLI 脚本

package.json 中添加 bin 字段:

json
{
  "name": "my-cli",
  "version": "1.0.0",
  "description": "A simple CLI tool",
  "bin": {
    "my-cli": "./my-cli.js"
  },
  "dependencies": {
    "commander": "^10.0.0"
  }
}

确保脚本具有可执行权限:

bash
chmod +x my-cli.js
sh
bash: /usr/local/bin/snippet: Permission denied<br/>
chmod +x cli.js

真整洁! npm install -g 实际上是将我们脚本链接到 path 变量的位置,所以我们能够在任何地方使用它。

sh
$ which snippet
/usr/local/bin/snippet
$ readlink /usr/local/bin/snippet
../lib/node_modules/bitbucket-snippet/cli.js

在开发环境中我们实际上使用 npm link 便利地将我们的 cli.js 软链接到 path 变量的位置。

sh
$ npm link
/usr/local/bin/snippet -> /usr/local/lib/node_modules/bitbucket-snippet/cli.js
/usr/local/lib/node_modules/bitbucket-snippet -> /Users/kannonboy/src/bitbucket-snippet

当我们开发完成的时候,我们可以通过 npm publish 将我们的脚本发布到公共 npm 仓库,然后任何人都可以下载安装到他们的机器上:

sh
$ npm install -g bitbucket-snippet

但是让我们先让我们的脚本能够工作先!

3.2 发布到 npm

  1. 登录 npm:
bash
npm login
  1. 发布到 npm:
bash
npm publish
  1. 全局安装并使用:
bash
npm install -g my-cli
my-cli --help

4. 美化输出

使用库如 chalkora 来增加颜色和加载动画。

  • chalk:控制台文字着色。
  • ora:控制台加载动画。
bash
npm install chalk ora

示例:

javascript
const chalk = require('chalk')
const ora = require('ora')

const spinner = ora('Processing...').start()

setTimeout(() => {
  spinner.succeed(chalk.green('Done!'))
}, 2000)

通过这些工具和方法,你可以快速创建一个功能丰富、用户友好的 CLI 工具。

Reference