Nodejs开发之Command Line Interface 命令行接口

首先,我们会使用Node内置的API来学习命令行基础知识;然后我们会使用commander模块来扩展基础知识。

一.命令行参数

命令行参数是一种基本的方式,来为计算机程序提供输入。在Node应用中,命令行参数可以通过process这个全局对象的argv属性来获得(数组类型)。下面的代码展示了如何使用forEach()这个方法来遍历argv

process.argv.forEach(function(arg,index){
    console.log("argv["+index+"] ="+ arg);
});

将代码保存在javascript文件中,命名为argv.js。在终端运行程序:

$ node argv.js -foo 3 --bar=4 -baz
argv[0] =/usr/local/bin/node
argv[1] =/Users/xxxx/Desktop/argv.js
argv[2] =-foo
argv[3] =3
argv[4] =--bar=4
argv[5] =-baz

其中,argv数组的前两个元素是不变的:分别是node和js文件的路径。

1.解析参数值:

我们传入了三个参数,分别是:foo、bar、baz;但是它们工作方式都不一样,参数foo的值为3,跟在参数名后面;bar的值为4,与bar处在同一个参数中,跟在=符号后面;而baz是Boolean类型的参数,如果参数被提供则为true,否则为false。

为了提取出正确的命令行参数值,我们可以开发一个自定义的解析器。在下面的代码中,parseArgs()函数负责解析命令行,提取值,返回可以将参数映射成值的对象。

function parseArgs() {
    var argv = process.argv;
    var args = {
        baz : false
    };
    for (var i = 0,len = argv.length; i < len; i++) {
        var arg = argv[i];
        var match;

        if(arg === "-foo"){
            args.foo = parseInt(argv[++i]);
        }else if (arg === "-baz"){
            args.baz = true;
            //使用正则表达式来解析
        }else if (match = arg.match(/--bar=(\d+)/)){
            args.bar = parseInt(match[1]);
        }
    }
    return args;
}

var args = parseArgs();
console.log(args);

运行结果如下:

$ node argv.js -foo 3 --bar=4 -baz
argv[0] =/usr/local/bin/node
argv[1] =/Users/xxx/Desktop/argv.js
argv[2] =-foo
argv[3] =3
argv[4] =--bar=4
argv[5] =-baz
{ baz: true, foo: 3, bar: 4 }

JavaScript内置的isFinite()方法用来判断参数是否为有效的整数。

2.commander模块中的命令行参数

第三方模块,commander,用来简化CLI(命令行接口)任务,例如参数解析、读取用户输入。使用npm install commander来安装commander模块,为了完成命令行参数解析,commander提供了option()parse()方法。每次调用option()方法都会向commander注册一个合理的命令行参数,所有的参数注册好后,parse()方法就会用来从命令行中提取参数值。

看下示例代码:

var commander = require("commander");

commander
    .option("-f,--foo ","Integer value for foo",parseInt,0)
    .option("-b,--bar ","Integer value for bar",parseInt,0)
    .option("-z,--baz","Boolean argument baz")
    .parse(process.argv);


console.log(commander.foo);
console.log(commander.bar);
console.log(commander.baz);

运行效果:

$ node command.js -f 4 -b 3 -z
4
3
true

该程序接收三个参数--foo--bar--baz--foo参数也可以写成-f,这是参数的缩写形式。所有的commander参数必须要有一个缩写名称和一个全称。缩写名称是一个” - “符号加上一个字母。全称是” – “加上名称。

注意,跟在--foo--bar后面的,这是跟在参数后面的值。使用尖括号” <> “代表值必须要提供,否则会出现错误;中括号” [] “可以提供,也可以不提供。--baz是Boolean类型的参数,因为它不会接收任何的参数。跟随参数字符串后面的是描述性字符串。

需要指出的是,--foo--bar也涉及到了parseInt()函数和数字0。parseInt()函数作为一个参数被传递到option()函数中,是用来处理额外的参数的,而额外的参数是作为parseInt()。在上述代码中,--foo--bar的值是整数,如果没有为--foo--bar提供值的话,就被设置为0。

所有的选项被注册后,就调用parse()来处理命令行。一般传入的是process.argv,解析后,参数的值就可以通过它们的全称来获取。

再看一下自定义的处理额外参数的代码:

var commander = require("commander");

var executeFoo = function (input){
    console.log("executeFoo",input);
}

var executeBar = function (input){
    console.log("executeBar",input);
}

commander
    .option("-f,--foo ","Integer value for foo",executeFoo,"default-foo")
    .option("-b,--bar ","Integer value for bar",executeBar,"default-bar")
    .option("-z,--baz","Boolean argument baz")
    .parse(process.argv);

运行结果为:

$ node command.js -f wind -b rain
executeFoo wind
executeBar rain

二.学习Commander

1.选项解析

选项使用.option()方法定义的,也会作为选项的文档。

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 souce')
    .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);

在上述代码中,--bbq-sauce是驼峰匹配(camel-cased),即bbqSauce。

2.对参数进行处理

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] ')
    .option('-i,--integer ','An integer argument',parseInt)
    .option('-f,--float ','A float argument',parseFloat)
    .option('-r,--range ..','A range',range)
    .option('-l,--list ','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 in incread',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);

运行结果:

$ node command.js -i 2 -f 4.4 -o 3 -r 2..4 -l 1,2,3,4  -c 4 -v 
int : 2
float : 4.4
optional : "3"
range: 2..4
list : ["1","2","3","4"]
collect: ["4"]
verbosity : 1
args:[]

3.正则表达式(Regular Expression)

var program = require("commander");

program
    .version('0.0.1')
    .option('-s --size ','Pizza size',/^(large|medium|small)$/i,'medim')
    .option('-d, --drink [drink]','Drink',/^(coke|pepsi|izze)$/i)
    .parse(process.argv);
console.log('size : %j',program.size);
console.log('drink: %j',program.drink);

运行结果:

$ node command.js -s large -d 
size : "large"
drink: true

$ node command.js -s larg -d cok
size : "medim"
drink: true

4.可变参数(Variadic arguments)

命令的最后一个参数(也只能是最后一个参数)可以是可变参数,为了让一个参数是可变的,你需要在参数名后面添加”...“。

示例代码如下:

var program = require("commander");

program
    .version('0.0.1')
    .command('rmdir  [otherDirs...]')
    .action(function(dir,otherDirs){

        console.log('rmdir %s',dir);
        if(otherDirs){
            otherDirs.forEach(function (oDir) {
                console.log('rmdir %s',oDir);
            });
        }

    });

program.parse(process.argv);

运行结果如下:

$ node command.js rmdir a.txt b.html c.doc
rmdir a.txt
rmdir b.html
rmdir c.doc

5.指定参数语法

var program = require("commander");

program
    .version('0.0.1')
    .arguments(' [env]')
    .action(function(cmd,env){
        cmdValue = cmd;
        envValue = env;
    });

program.parse(process.argv);

if(typeof cmdValue === 'undefinee') {
    console.error('no command given');
    procee.exit(1);
}

console.log('command:', cmdValue);
console.log('enviroment:',envValue||"no enviroment given");

运行结果:

$ node command.js love you
command: love
enviroment: you

$ node command.js love 
command: love
enviroment: no enviroment given

$ node command.js 
/Users/weichuang/Desktop/Node/command.js:19
console.log('command:', cmdValue);
                        ^

ReferenceError: cmdValue is not defined

6.Git样式的子命令

示例代码:

var program = require("commander");

program
    .version('0.0.1')
    .command('install [name]','install one or more packages')
    .command('search [query]','search with optional query')
    .command('list','list packages installed',{isDefault:true})
    .parse(process.argv);

.command()被调用时,不应当调用.action(callback即回调函数)来处理子命令,否则会出现错误。这会告诉commander你会使用分开的程序来处理子命令。

commander会在入口脚本所在的那个目录搜索可执行程序,名字格式为”program-command“,例如,command-installcommand-search。(其中,command是程序的名称,installsearch是子命令)。

选项可以传递给.command()。将opts.noHelp设置为true会将选项从产生的帮助输出中移除,将opts.isDefault设置true:如果其它子命令没有设置,这个命令会运行。

如果程序是被全局安装的,确保可执行的程序有正确的模式,例如755。

7.自动产生的帮助

帮助信息是自动生成的,建立在commander对你的程序所了解的情况下,所以下面的--help信息是自动产生的:

$ node command.js --help

  Usage: command [options] [command]


  Commands:

    install [name]  install one or more packages
    search [query]  search with optional query
    list            list packages installed
    help [cmd]      display help for [cmd]

  Options:

    -h, --help     output usage information
    -V, --version  output the version numbe

8.自定义帮助

你可以通过监听”–help”来展示自定义的-h,--help信息,一旦完成展示帮助信息,commander就会自动退出程序,例如,下面的”stuff”不会被输出。

var program = require("commander");

program
    .version('0.0.1')
    .option('-f,--foo','enable some foo')
    .option('-b,--bar','enable some bar');

program.on('--help',function(){
    console.log('Following is the help information :');
    console.log(' $ -f --foo  eat foo');
    console.log(' $ -b --bar  eat bar');
});

program.parse(process.argv);

console.log('stuff');

运行结果:

$ node command.js --help

  Usage: command [options]

  Options:

    -h, --help     output usage information
    -V, --version  output the version number
    -f,--foo       enable some foo
    -b,--bar       enable some bar

Following is the help information :
 $ -f --foo  eat foo
 $ -b --bar  eat bar

9.”.outputHelp(cb)

这种做法会输出帮助信息,但是不会退出程序。可选的回调函数允许在帮助文本展示之前对其进行加工。


var program = require("commander");
var colors = require("colors");

program
    .version('0.0.1')
    .command('getstream [url]','get stream URL')
    .parse(process.argv);

if(!process.argv.slice(2).length){
    console.log('red');
    program.outputHelp(make_red);
}

function make_red(txt) {
    return colors.red(txt);
}

10.示例代码

var program = require("commander");

program
    .version('0.0.1')
    .option('-C,--chdir ','change the working directory')
    .option('-c,--config ,'set config path.defaults to ./deploy.conf')
    .option('t,--no-tests','ignore test hook');


program
    .command('setup [env]')
    .description('run setup commands for all envs')
    .option('-s,--setup_mode [mode]','which setup mode to use')
    .action(function(env,options){
        var mode = options.setup_mode || "normal";
        env = env || "all";
        console.log('setup for %s env(s) with %s mode',env,mode);
    });

program
    .command('exec ')
    .alias('ex')
    .description('execute the given remote cmd')
    .option('-e,--exec_mode ','which setup mode to use')
    .action(function(cmd,options){
        console.log("exec %s using %s mode", cmd,options.exec_mode);
    }).on('--help',function(){
        console.log(' exec-help');
        console.log('$delpoy exec');
    });

program
    .command('*')
    .action(function(env){
        console.log('deploying %s',env);
    })

program.parse(process.argv);

运行结果:

$ node command.js --help

  Usage: command [options] [command]


  Commands:

    setup [options] [env]    run setup commands for all envs
    exec|ex [options]   execute the given remote cmd
    *                      

  Options:

    -h, --help         output usage information
    -V, --version      output the version number
    -C,--chdir   change the working directory
    -c,--config 
    t,--no-tests       ignore test hook
$ node command.js setup hello world
setup for hello env(s) with normal mode

三.从零制作一个可以发布到npm官网的node包

1.创建一个文件夹,命名为remem,在终端进入该目录下。

2.使用npm init命令来创建package.json文件以及完成相关文件的配置。

Nodejs开发之Command Line Interface 命令行接口_第1张图片

3.在当前目录下创建一个bin文件夹,并在里面创建一个remem.js文件。

Nodejs开发之Command Line Interface 命令行接口_第2张图片

4.在package.json文件中进行配置,添加:"bin": { "remem": "bin/remem.js" },

Nodejs开发之Command Line Interface 命令行接口_第3张图片

5.在制作包时,我们需要两个第三方模块:分别是commander和sqlite3。在终端进入根目录(即package.json所在的目录),通过命令安装这两个模块。

$ sudo npm install sqlite3
$ sudo npm install commander

6.在package.json中配置这两个依赖项,添加:"dependencies":{
"commander":"~2.9.0",
"sqlite3":"~3.1.1"
},

Nodejs开发之Command Line Interface 命令行接口_第4张图片

7.这一步当然是写代码,具体根据自己的需求来写….

8.配置全局运行命令

在项目目录(即package.json所在目录)下,运行 npm link 会自动添加全局的 symbolic link ,然后就可以使用自己的命令了。

这是我开发的npm模块remem

你可能感兴趣的:(Node.js)