Skip to content

commander 教程

通过案例学习 commander

split.js

js
// 引入 commander
const { program } = require('commander')

// program.option() 定义一个选项。用户执行脚本命令携带的选项必须能对应传入 option 的参数。 
// 换句话说,脚本只能携带 option 里定义的选项,如果不是 option 里的参数会抛错。 
program
  // 接收脚本传递 --first 选项。如果传了,返回的 option 对象中包含属性 `first: true`,否则不包含该属性。
  .option('--first')
  // 接收脚本传递 --separator 或缩写 -s 选项,并且 -s 选项后必须传参 <char>。
  // `<>` 代表必须传参,char 代表参数名,命令随意。
  .option('-s, --separator <char>')

// 解析用户传入的脚本命令,默认为 process.argv。
// 然后就能使用 program.opts() api 了。
// program.parse()

// console.log(process.argv)
// [
//   'D:\\Installation\\nvm\\v16.18.0\\node.exe',
//   'D:\\Code\\my-course\\docs\\useful\\commander-demo\\split.js',       
//   '-s',
//   '123'
// ]

// program.parse 传入的第一个参数类型为 string[],并且默认从数组的第三为元素开始解析。
// 为什么默认从第三位开始解析?因为默认情况下,process.argv 的第三位及以后元素才是用户传递的参数。
// 所以如果第二个参数传入 { from: 'user' },代表直接从第一位开始解析。

// 成功
// program.parse(['11', '-s', '89'], { from: 'user' })

// 成功,'11' 被当做无用的参数,push 到 program.args 里。
program.parse(['11', '-s', '89'], { from: 'user' })

// 成功,'123r35' 被作为没有用到的参数
// program.parse(['-s', '89', '123r35'], { from: 'user' })

// 失败,error: unknown option '--asdf'
// --asdf 被当做一个选项,但是 program 并没有定义该选项
// program.parse(['11', '-s', '89', '--asdf'], { from: 'user' })

// 获取脚本参数的对象格式。
const options = program.opts()
console.log(`'options 对象:'`, options)

// 没有用到的参数
/** type: string[] */
const params = program.args
console.log(`'没有被用到的参数'`, params)

尝试

shell
$ node split.js -s 123 --first
'options 对象:' { separator: '123', first: true }

Commander 的全局 program 对象和示例对象

如果程序较为简单,可以直接使用 commander 提供的 program 对象。

js
// CommonJS (.cjs)
const { program } = require('commander');

如果程序较为复杂,也可以通过 new Command() 来实例化一个 program 对象。

js
// CommonJS (.cjs)
const { Command } = require('commander');
const program = new Command();

选项

Commander 使用 .option() 方法来定义选项。第一个参数传入选项的定义,第二个参数传入选项的介绍。

第一个参数可以传入一个短选项名称(-后面接单个单词)和一个长选项名称(--后面接一个单词或由短横线组成的多个单词)。短选项名称和长选项名称使用逗号 , 或空格 或管道符 | 分隔开。

解析后的选项可以通过 Commander 对象上的 .opts() 方法获取。对于由短横线组成的多个单词的长选项,选项名会被转为驼峰命名法。比如 --template-engine 选项可以通过 program.opts().templateEngine 获取。

选项和选项参数可以用空格分隔。长选项可以直接在后面加上 = 然后传递参数。

serve -p 80
serve --port 80
serve --port=80

默认情况下,选项在命令行中的顺序不固定,一个选项可以在其它参数之前或之后指定。

boolean 型选项和带参数选项

boolean 型选项不需要带参数。

其它类型选项可以设置是否需要带参数。

options-common.js

js
const { program } = require('commander')

program
  // 带参数型选项,且必须传参,否则抛错。
  .option('-e, --eat <what>', '我打算吃点什么')
  // boolean 型选项
  .option('-s, --sleep', '我打算睡觉')
  // 带有默认参数选项,如果命令中没有选项,那么将默认值作为参数,使用该选项。
  // 它的意思并不是指使用该选项可以不传参,而是 opts 中必定包含该选项。
  .option('-p, --play <what>', '我打算玩点什么', '王者荣耀')

// program.parse(['-e', '肉末茄子', '-s'], { from: 'user' })
// 成功。
// { play: '王者荣耀', eat: '肉末茄子', sleep: true }

// program.parse(['-e', '肉末茄子', '-s', '-p'], { from: 'user' })
// 失败。
// 因为虽然 -p 选项有默认值,但如果你传入 parse 的参数中有 '-p',那么就得明确指定它的参数。

const options = program.opts()
console.log(options)

取反选项

定义选项时带有 no-,传入的命令参数选项也必须带有 no-,得到的 opts() 中,选项名不包含 no-

js
const { Command } = require('commander')

const program = new Command()

program
  .option('--no-sauce', '默认有酱汁')
  .option('--no-shallot', '默认加葱')
  .option('--no-ginger', '默认加姜')
  .option('--no-garlic', '默认加蒜')

// 不要酱汁,不要葱姜蒜
program.parse(['--no-sauce', '--no-shallot', '--no-ginger', '--no-garlic'], { from: 'user' })
// { sauce: false, shallot: false, ginger: false, garlic: false }

const options = program.opts()
console.log(options)

可选选项

js
const program2 = new Command()

program2.option('-a [abc]', '可选参数')

// program2.parse(['-a', '11'], {from: 'user'})
// { a: '11' }

program2.parse(['-a'], {from: 'user'})
// { a: true }

console.log(program2.opts())

必填选项

通过.requiredOption() 方法可以设置选项为必填。必填选项要么设有默认值,要么必须在命令行中输入且带有参数,对应的属性字段在解析时必定会有赋值。

js
program
  .requiredOption('-c, --cheese <type>', 'pizza must have cheese');

program.parse();

传入多个参数

定义选项时,可以通过使用...来设置参数为可变长参数。在命令行中,用户可以输入多个参数,解析后会以数组形式存储在对应属性字段中。在输入下一个选项前(-或--开头),用户输入的指令均会被视作变长参数。与普通参数一样的是,可以通过--标记当前命令的结束。

js
program
  .option('-n, --number <numbers...>', 'specify numbers')
  .option('-l, --letter [letters...]', 'specify letters');

program.parse();

console.log('Options: ', program.opts());
console.log('Remaining arguments: ', program.args);

action handler

js

const { Command } = require('commander')

const program = new Command()
program
  .option('--so')
  .argument('<username>', 'user to login')
  .argument('[password]', 'password for user, if required', 'no password given')
  .argument('[nickName]', '昵称', '小瘪三')
  // 传入的参数依次为你声明的每一个命令(除了 option)。
  // 最后还有额外两个参数,options 和 command(被解析的选项对象和命令对象本身)。
  .action((username, password, nickName, options, command) => {
    // 2443876261
    console.log('username:', username);
    // 123456
    console.log('password:', password);
    // nickName
    console.log('nickName', nickName)
    // options
    // { so: true } { so: true }
    console.log(options, command.opts())
  });

program.parse(['--so', '2443876261', '123456'], { from: 'user' })

Released under the MIT License.