Node.js 命令行实现
使用 Node.js 实现命令行。
命令行参数处理
yargs
简单模式
只需要引入 yargs,就能读取命令行参数,不需要写任何的配置
#!/usr/bin/env node
var argv = require('yargs').argv;
console.log('hello ', argv.name);
$ hello --name=tom
hello tom
$ hello --name tom
hello tom
简单模式还能读取短变量如 -x 4
相当于 argv.x = 4
简单模式还能读取布尔类型 -s
相当于 argv.s = true
简单模式还能读取非 -
开始的变量,这种类型的变量保存在 argv._
数组里面
count 统计变量出现的次数
var argv = require('yargs')
.count('num')
.alias('n', 'num')
.argv;
统计 num
参数出现的次数,缩写 -n
也会统计进去
node count.js -n
node count.js -nn
node count.js -n --num
demand default describe
var argv = require('yargs')
.demand(['n']) //是否必选
.default({n: 'tom'}) //默认值
.describe({n: 'your name'}) //提示
.argv;
n
参数不可省略,默认值为 tom
,并给出一行提示
options 将所有配置写进一个对象
var argv = require('yargs')
.option('n', {
alias : 'name',
demand: true,
default: 'tom',
describe: 'your name',
type: 'string'
})
.argv;
boolean 方法指定参数返回布尔值
var argv = require('yargs')
.boolean(['l'])
.argv;
帮助信息
- usage:用法格式
- example:提供例子
- help:显示帮助信息
- epilog:出现在帮助信息的结尾
var argv = require('yargs')
.option('f', {
alias : 'name',
demand: true,
default: 'tom',
describe: 'your name',
type: 'string'
})
.usage('Usage: hello [options]')
.example('hello -n tom', 'say hello to Tom')
.help('h')
.alias('h', 'help')
.epilog('epilog 2017')
.argv;
结果
$ hello -h
Usage: hello [options]
Options:
-f, --name your name [string] [required] [default: "tom"]
-h, --help Show help [boolean]
Examples:
hello -n tom say hello to Tom
epilog 2017
设置子命令 command()
.command(cmd, desc, [builder], [handler])
.command(module)
.command(cmd, desc, [module])
//.command(cmd, desc, [builder], [handler])
yargs
.command(
'get',
'get incident',
function (yargs) {
return yargs.option('u', {
alias: 'url',
describe: 'the url get incident'
})
},
function (argv) {
console.log(argv.url)
}
)
.help()
.argv
//.command(module) test.js
exports.command = 'get'
exports.describe = 'get incident'
exports.builder = {
name: {
default: 'tom'
}
}
exports.handler = function (argv) {
//console.log(argv)
}
yargs.command(require('drafts/test'))
.help()
.argv
commandDir
如果有大量的命令都使用上面的 command(module)
来开发的话,这些模块都有相同的结构,yargs 提供了 commandDir
接口,简化这些命令的引入过
程,把这个过程自动化.
require('yargs')
.commandDir('commands')
.demand(1)
.help()
.locale('en')
.showHelpOnFail(true, 'Specify --help for available options')
.argv
commandDir
默认加载目录下第一级的文件,递归加载:commandDir('pit', {recurse: true})
commander
commander
是从 Ruby 下同名项目移植过来的,官方文档 。
commander 特性
- 自记录代码
- 自动生成 help
- 合并短参数(“ABC”==“-A-B-C”)
- 默认选项
- 强制选项
- 命令解析
- 提示符
example
\\example test.js file:
var commander = require('commander');
commander
.version('1.0.0')
.usage('[options] [value ...]')
.option('-l, --langu', 'langueage')
.parse(process.argv);
if (commander.langu == 'zh-cn') {
console.log('Chinese website!');
}else if(commander.langu == 'en') {
console.log('English website!');
}
启动 test.js 时,接受一个 -l
的参数
$node test.js -l en
commander API
Option()
: 初始化自定义参数对象,设置“关键字”和“描述”Command()
: 初始化命令行参数对象,直接获得命令行输入Command#command()
: 定义一个命令名字Command#action()
: 注册一个callback
函数Command#option()
: 定义参数,需要设置“关键字”和“描述”,关键字包括“简写”和“全写”两部分,以,
,|
,空格
做分隔。Command#parse()
: 解析命令行参数argv
Command#description()
: 设置description
值Command#usage()
: 设置usage
值Command#version()
: 指定当前应用程序的一个版本号
参数解析
1.使用 option()
方法自定义参数;
2.使用 parse()
方法解析用户从命令行输入的参数。
上面的例子中: parse()
方法对 option()
方法定义的参数进行赋值,然后将剩下的参数(未定义的参数)赋值给 commander 对象的 args
属性
(program.args
),program.args
是一个数组。
command()
、description()
和 action()
command()
方法有点复杂,最常用的方法是和 action()
联合起来用:
var program = require('commander');
program
.version('0.0.1')
.option('-C, --chdir <path>', 'change the working directory')
.option('-c, --config <path>', 'set config path. defaults to ./deploy.conf')
.option('-T, --no-tests', 'ignore test hook')
program
.command('setup') // 定义命令
.description('run remote setup commands') // 对命令参数的描述信息
.action(function() { // setup命令触发时调用
console.log('setup');
});
program
.command('exec <cmd>') // 定义命令,参数可以用 <> 或 [] 修饰
.description('run the given remote command')
// exec 触发时调用,参数和定义的参数顺序一致
.action(function(cmd) {
console.log('exec "%s"', cmd);
});
program
.command('init') // 定义命令,参数可以用 <> 或 [] 修饰
.description('run the given remote command')
.option('-y, --yes', 'without prompt')
// options 是 init 命令的参数对象、是 action 回调方法的最后一个参数
.action(function(options) {
console.log('init param "%s"',options.yes );
});
program.parse(process.argv);
可变参数
一个命令的最后一个参数可以是可变参数, 并且只能是最后一个参数。可变参数需要在参数名后面追加 ...
:
var program = require('commander');
program
.version('0.0.1')
.command('rmdir <dir> [otherDirs...]')
.action(function(dir, otherDirs) {
console.log('rmdir %s', dir);
if (otherDirs) {
//otherDirs是一个数组
otherDirs.forEach(function(oDir) {
console.log('rmdir %s', oDir);
});
}
});
program.parse(process.argv);
commander 帮助信息
默认情况下,commander 会根据 option()
方法的参数为你帮你实现 --help
参数,当用户在命令行使用 -h
或 --help
参数时,将自动打印
出帮助信息。也支持自定义帮助信息:
commander.on('help', function() { // 自定义帮助信息
console.log(' # website langueage ')
console.log(' $ ./app.js -l \"a string zh-cn or en \" ')
console.log('')
});
# or
commander.on('--help', function(){ // 自定义帮助信息
console.log(' Examples:');
console.log('');
console.log(' $ custom-help --help');
console.log(' $ custom-help -h');
console.log('');
});
主动打印帮助信息
两种情况:
- 打印帮助信息,然后等待用户继续输入信息,不结束当前进程——
program.outputHelp()
- 打印帮助信息,并立即结束当前进程——
program.help()
.outputHelp()
方法和 .help()
方法都可以带一个参数——一个回调方法,打印信息在显示到命令行之前会先传入这个方法,你可以在方法里面做必要
的信息处理,比如改变文本颜色。
argparse
\\example test.js file:
import {ArgumentParser} from 'argparse';
const parser = new ArgumentParser({
version: require("../package.json").version,
addHelp: true,
description: "Storage Server"
});
parser.addArgument(["-rpc","--rpcPort"], {
required: false,
help: "rpc port",
dest: "rpcPort"
});
parser.addArgument("--isLocal", {
dest: "isLocal",
help: "is publish from office network",
defaultValue: false,
action: "storeTrue"
});
const argument = parser.parseArgs();
console.log(argument.rpcPort)
console.dir(args);
$ ./test.js -h
usage: example.js [-h] [-rpc] [--isLocal]
Storage Server
Optional arguments:
-h, --help Show this help message and exit.
-v, --version Show program's version number and exit.
-rpc, --rpcPort rpc port
--isLocal is publish from office network
shell.js
shelljs
模块重新包装了 child_process
,调用系统命令更加简单。官方文档 。
安装
npm install shelljs --save
使用
shelljs
绝大部分命令都是对文件和文件夹的操作。看个例子:
var shell = require('shelljs');
// 判定 git 命令是否可用
if (!shell.which('git')) {
// 向命令行打印git命令不可用的提示信息
shell.echo('Sorry, this script requires git');
// 退出当前进程
shell.exit(1);
}
// 先删除 'out/Release' 目录
shell.rm('-rf', 'out/Release');
// 拷贝文件到 'out/Release' 目录
shell.cp('-R', 'stuff/', 'out/Release');
// 切换当前工作目录到 'lib'
shell.cd('lib');
// shell.ls('*.js') 返回值是一个包含所有 js 文件路径的数组
shell.ls('*.js').forEach(function(file) {//遍历数组
// sed 命令用于文件内容的替换,这里是对每个文件都执行如下 3 步操作,更改版本信息
shell.sed('-i', 'BUILD_VERSION', 'v0.1.2', file);
shell.sed('-i', /^.*REMOVE_THIS_LINE.*$/, '', file);
shell.sed('-i', /.*REPLACE_LINE_WITH_MACRO.*\n/, shell.cat('macro.js'), file);
});
// 切换当前工作目录到上一层
shell.cd('..');
// 同步执行 git 命令提交代码
if (shell.exec('git commit -am "Auto-commit"').code !== 0) {
shell.echo('Error: Git commit failed');
shell.exit(1);
}
上面的例子展示了一个可发布版本提交到 git
仓库的过程。
exec
- exec(command [, options] [, callback])
command <String>
: 要在命令行执行的完整命令options <Object>
: 可选参数,JSON
对象async
: 异步执行.如果你提供了回调方法,这个值就一定为true
,无论你怎么设置silent
: 打印信息不输出到命令控制台- Node.js 的
child_process.exec()
方法的其他参数都可以用
callback:<Function>
: 当进程终止时调用,并带上输出。error <Error>
stdout <String> | <Buffer>
stderr <String> | <Buffer>
返回值:同步模式下,将返回一个ShellString
(shelljs v0.6.xf 返回一个形如{ code:..., stdout:... , stderr:... }
的对象; 异步模式下,将返回一个child_process
的对象
exec()
同步方法的实现会占用大量 CPU,所以建议使用异步模式。
例:
var version = exec('node --version', {silent:true}).stdout;
var child = exec('some_long_running_process', {async:true});
child.stdout.on('data', function(data) {
/* ... do something with data ... */
});
exec('some_long_running_process', function(code, stdout, stderr) {
console.log('Exit code:', code);
console.log('Program output:', stdout);
console.log('Program stderr:', stderr);
});
命令行交互
命令行交互可以使用 prompt 实现。
const prompt = require('prompt');
var schema = {
properties: {
name: {
pattern: /^[a-zA-Z\s\-]+$/,
message: 'Name must be only letters, spaces, or dashes',
required: true
},
password: {
hidden: true
}
}
};
//
// Start the prompt
//
prompt.start();
//
// Get two properties from the user: email, password
//
prompt.get(schema, function (err, result) {
//
// Log the results.
//
console.log('Command-line input received:');
console.log(' name: ' + result.name);
console.log(' password: ' + result.password);
});
隐式调用 node 程序
在执行 node 程序的时候都是使用 node filePath
(比如,node run.js
)的形式。如何像 npm,git 一样直接调用?
- 在你要运行的脚本文件头添加
#!/usr/bin/env node
,例如:
#!/usr/bin/env node
console.log('Hello,world!');
它的作用是指定脚本文件的执行程序 node。做完上面的修改,可以直接 ./<file>
了。
- 如果想要一个全局的命令,在
package.json
中加入一个字段bin
,注意:
"bin":{
"myCmd":"<file path>"
},
myCmd 就是你命令的名字,如果是准备发布的模块,一般是会使用模块名称。
- 在当前工作目录下执行
npm link
,把模块链接的本地 npm 仓库,然后就可以全局调用 myCmd 了。