Posted at

NodeでCLIツール作成時にCommander.jsを使うと便利だった話

More than 3 years have passed since last update.

NodeでCLIツールを作る際に Commander.js を使うとよく使うオプションコマンドの指定など容易に設定できて重宝したのでメモ。


参考


できること



  • --versionの指定

  • オプションの指定。-v--verboseなどのように一文字のものと複数文字列の指定も可能

  • オプションに従い、自動で--helpの文言を作成

  • オプションの指定時に引数も指定できる。--id 1など。-l 1,2,3のようにリストなどの引数も取得できる

  • オプション引数の任意指定、必須指定

  • オプション引数のデフォルト値の設定

  • 正規表現によるオプション引数の確認

他にも色々できそう(gitみたいにサブコマンドを作るなど)できそうですが、今回はそこは未確認です。時間があれば別途。


準備

$npm install commander


オプションの設定

.option()メソッドを使うことでCLIのオプションの定義ができます。

定義したオプションの内容は自動でhelpの際に表示ができます


index.js

#!/usr/bin/env node


/**
* Module dependencies.
*/

var program = require('commander');

program
.version('0.0.1')
.option('-p, --peppers', 'Add peppers')
.option('-P, --pineapple', 'Add pineapple')
.option('-b, --bbq-sauce', 'Add bbq sauce')
.option('-c, --cheese [type]', 'Add the specified type of cheese [marble]', 'marble')
.parse(process.argv);

console.log('you ordered a pizza with:');
if (program.peppers) console.log(' - peppers');
if (program.pineapple) console.log(' - pineapple');
if (program.bbqSauce) console.log(' - bbq');
console.log(' - %s cheese', program.cheese);


使うときはこんな感じ。

$node index.js --version

0.0.1

$node index.js --help

Usage: index [options]

Options:

-h, --help output usage information
-V, --version output the version number
-p, --peppers Add peppers
-P, --pineapple Add pineapple
-b, --bbq-sauce Add bbq sauce
-c, --cheese [type] Add the specified type of cheese [marble]

# --cheseについてはデフォルト値が設定されている。optionメソッドの第3引数に指定する
$node index.js
you ordered a pizza with:
- marble cheese

# -pオプションと--pepersオプションは同じ意味。
$node index.js --peppers
you ordered a pizza with:
- peppers
- marble cheese

$node index.js -p
you ordered a pizza with:
- peppers
- marble cheese


オプションの引数の指定

以下、公式のサンプルコード。


index.js

#!/usr/bin/env node


/**
* Module dependencies.
*/

var program = require('commander');

function range(val) {
return val.split('..').map(Number);
}

function list(val) {
return val.split(',');
}

function collect(val, memo) {
memo.push(val);
return memo;
}

function increaseVerbosity(v, total) {
return total + 1;
}

program
.version('0.0.1')
.usage('[options] <file ...>')
.option('-i, --integer <n>', 'An integer argument', parseInt)
.option('-f, --float <n>', 'A float argument', parseFloat)
.option('-r, --range <a>..<b>', 'A range', range)
.option('-l, --list <items>', 'A list', list)
.option('-o, --optional [value]', 'An optional value')
.option('-c, --collect [value]', 'A repeatable value', collect, [])
.option('-v, --verbose', 'A value that can be increased', increaseVerbosity, 0)
.parse(process.argv);

console.log(' int: %j', program.integer);
console.log(' float: %j', program.float);
console.log(' optional: %j', program.optional);
program.range = program.range || [];
console.log(' range: %j..%j', program.range[0], program.range[1]);
console.log(' list: %j', program.list);
console.log(' collect: %j', program.collect);
console.log(' verbosity: %j', program.verbose);
console.log(' args: %j', program.args);


オプション引数の必須指定、任意指定の設定ができる。

# optionメソッドの第2引数で<n>というように<>と指定するとオプションの引数が必須となる

$node index.js -i

error: option `-i, --integer <n>' argument missing

# [value]というように[]とするとオプションの引数は任意

$node index.js -o
int: undefined
float: undefined
optional: true
range: undefined..undefined
list: undefined
collect: []
verbosity: undefined
args: []

$node index.js -o optional
int: undefined
float: undefined
optional: "optional"
range: undefined..undefined
list: undefined
collect: []
verbosity: undefined
args: []

オプション引数で様々な型の値を取得できる

# optionメソッドの第3引数にfunctionを設定した場合、その関数が実行される.intの場合にはparseInt

$node index.js -i 1
int: 1
float: undefined
optional: undefined
range: undefined..undefined
list: undefined
collect: []
verbosity: undefined
args: []

# parseIntによってintのみ許容
$node index.js -i hoge
int: null
float: undefined
optional: undefined
range: undefined..undefined
list: undefined
collect: []
verbosity: undefined
args: []

# カンマ区切りのリスト.これは自分でlist関数を作ってそれを利用
$node index.js -l 1,2,3
int: undefined
float: undefined
optional: undefined
range: undefined..undefined
list: ["1","2","3"]
collect: []
verbosity: undefined
args: []

# collectには何も指定しなくても[]が設定されている。第3引数に関数を指定している場合には、第4引数がデフォルト値となる
$node index.js
int: undefined
float: undefined
optional: undefined
range: undefined..undefined
list: undefined
collect: []
verbosity: undefined
args: []


オプション引数の正規表現確認


index.js

program

.version('0.0.1')
.option('-s --size <size>', 'Pizza size', /^(large|medium|small)$/i, 'medium')
.option('-d --drink [drink]', 'Drink', /^(coke|pepsi|izze)$/i)
.option('-i --id [id]', 'SecurityGroupId', /^sg-[0-9a-z]*$/i)
.parse(process.argv);

console.log(' size: %j', program.size);
console.log(' drink: %j', program.drink);
console.log(' id: %j', program.id);


確認。

# 正規表現で指定されているもののみ指定可能

$node index.js -s large
size: "large"
drink: undefined

# 正規表現に合致しないのでデフォルト値が設定される
$node index.js -s lar
size: "medium"
drink: undefined

# 内部的にRegExp.execを結果の配列の[0]を返却しているのでなんでもできる
$node index.js --id sg-abc
size: "medium"
drink: undefined
id: "sg-abc"

# 合致しないとtrueが返る.undefinedかtrueならNGとするなどできる
$node index.js --id s-aaaa
size: "medium"
drink: undefined
id: true