使用nodejs书写命令行程序的简单框架

引子

本文使用Nodejs主要实现了如下形式的命令行程序

node index -A 100 //主命令
node index cmd1 -C 100 -D 200 //使用option的子命令
node index cmd2 param1 param2 param3 //使用argments的子命令
node index cmd3 -E 100 -F 200 param1 param2 //同时使用argments和option的子命令

文中option的取值通过options.option名的方式取得,option名在option定义时,通过--option名定义

导入第三方package

var co = require('co');
var program = require('commander');
var colors = require('colors');
  • co以同步的书写方式书写回调函数
  • commander解析命令行参数和选项,执行Action
  • colors修改输出文字颜色,美化控制台输出

书写流程控制函数

对于每一个命令,虽然执行内容不同,但执行流程如下所示大致相同

  • 使用co调用该命令的处理函数
  • 执行成功后输出success信息,退出程序
  • 执行失败后输出error信息,退出程序
var execute = function(target, args) {
  co(function*() {
    yield target.apply(this, args);
  }).then(function() {
    console.log(colors.green('[end]') + ' success');
    process.exit();
  }, function(error) {
    console.log(error);
    process.exit();
  });
};

书写各命令的处理函数

  • 主命令处理函数
    接受program作为入参,从中可获取指定的option的值。
var doMain = function*(program) {
  console.log(colors.green('[start]') + ' doMain1...');
  // do something
  console.log(program.aa);
  console.log(colors.green('[end  ]') + ' doMain1');
};
  • 子命令1的处理函数
    使用options作为入参,从中可获取指定的option的值。
var doCmd1 = function*(options) {
  console.log(colors.green('[start]') + ' doCmd1...');
  // do something
  console.log(options.cc + ',' + options.dd);
  console.log(colors.green('[end  ]') + ' doCmd1');
};
  • 子命令2的处理函数
    使用命令行参数作为入参
var doCmd2 = function*(arg1, arg2, arg3) {
  console.log(colors.green('[start]') + ' doCmd2...');
  // do something
  console.log(arg1 + ',' + arg2 + ',' + arg3);
  console.log(colors.green('[end  ]') + ' doCmd2');
};
  • 子命令3的处理函数
    使用命令行参数和options作为入参,options中可获取指定的option的值。
var doCmd3 = function*(arg1, arg2, arg3, options) {
  console.log(colors.green('[start]') + ' doCmd3...');
  // do something
  console.log(arg1 + ',' + arg2 + ',' + arg3 + ',' + options.ee + ',' +
    options.ff);
  console.log(colors.green('[end  ]') + ' doCmd3');
};

定义命令行参数,Option以及Action

  • 主命令定义
    定义程序的版本,option信息和帮助信息。
program
  .version('0.0.1');
program
  .description('使用node书写命令行程序的简单框架')
  .option('-A, --aa <optionA>', 'XXXX数.')
  .option('-B, --bb <optionB>', 'XXXX数.');
program.on('--help', function() {
  console.log('  Examples:');
  console.log('');
  console.log('    node index cmd1 -C 100 -D 200');
  console.log('    node index cmd2 param1 param2 param3');
  console.log('    node index cmd3 -E 100 -F 200 param1 param2');
  console.log('');
});
  • 子命令定义
    定义各命令的option和处理action,以及描述信息和帮助信息
program
  .command('cmd1')
  .description('只包含Option的子命令')
  .option('-C, --cc <optionC>', 'XXXX数.')
  .option('-D, --dd <optionD>', 'XXXX数.')
  .action(function() {
    execute(doCmd1, arguments);
  });
program
  .command('cmd2 <arg1> <arg2> [arg3]')
  .description('只包含命令行参数的子命令')
  .action(function() {
    execute(doCmd2, arguments);
  }).on('--help', function() {
    console.log('  arg1:');
    console.log('');
    console.log('    arg1的含义说明');
    console.log('');
  });
program
  .command('cmd3 <arg1> <arg2> [arg3]')
  .option('-E, --ee <optionE>', 'XXXX数.')
  .option('-F, --ff <optionF>', 'XXXX数.')
  .description('同时包含命令行参数和Option的子命令')
  .action(function() {
    execute(doCmd3, arguments);
  }).on('--help', function() {
    console.log('  arg1:');
    console.log('');
    console.log('    arg1的含义说明');
    console.log('');
  });

命令行解析和执行

program.parse(process.argv);

主程序流程控制

  • 设置主程序运行flag
var mainFlg = true;
  • 运行程序时,如果未指定任何子命令或option信息,输出帮助信息,不执行主命令
if (!process.argv.slice(2).length) {
  program.outputHelp(make_red);
  mainFlg = false;
}
function make_red(txt) {
  return colors.red(txt); //display the help text in red on the console
}
  • 运行程序时,如果指定了子命令,执行子命令,不执行主命令
var cmds = [];
program.commands.map(function(cmd) {
  cmds.push(cmd._name);
});
for (var arg of process.argv.slice(2)) {
  if (cmds.indexOf(arg) >= 0) {
    mainFlg = false;
  }
}
if (mainFlg) {
  execute(doMain, [program]);
}

完整的程序文件

'use strict';
var co = require('co');
var program = require('commander');
var colors = require('colors');
var execute = function(target, args) {
  co(function*() {
    yield target.apply(this, args);
  }).then(function() {
    console.log(colors.green('[end  ]') + ' success');
    process.exit();
  }, function(error) {
    console.log(error);
    process.exit();
  });
};
var doMain = function*(program) {
  console.log(colors.green('[start]') + ' doMain1...');
  // do something
  console.log(program.aa);
  console.log(colors.green('[end  ]') + ' doMain1');
};
var doCmd1 = function*(options) {
  console.log(colors.green('[start]') + ' doCmd1...');
  // do something
  console.log(options.cc + ',' + options.dd);
  console.log(colors.green('[end  ]') + ' doCmd1');
};
var doCmd2 = function*(arg1, arg2, arg3) {
  console.log(colors.green('[start]') + ' doCmd2...');
  // do something
  console.log(arg1 + ',' + arg2 + ',' + arg3);
  console.log(colors.green('[end  ]') + ' doCmd2');
};
var doCmd3 = function*(arg1, arg2, arg3, options) {
  console.log(colors.green('[start]') + ' doCmd3...');
  // do something
  console.log(arg1 + ',' + arg2 + ',' + arg3 + ',' + options.ee + ',' +
    options.ff);
  console.log(colors.green('[end  ]') + ' doCmd3');
};
program
  .version('0.0.1');
program
  .description('使用node书写命令行程序的简单框架')
  .option('-A, --aa <optionA>', 'XXXX数.')
  .option('-B, --bb <optionB>', 'XXXX数.');
program.on('--help', function() {
  console.log('  Examples:');
  console.log('');
  console.log('    node index cmd1 -C 100 -D 200');
  console.log('    node index cmd2 param1 param2 param3');
  console.log('    node index cmd3 -E 100 -F 200 param1 param2');
  console.log('');
});
program
  .command('cmd1')
  .description('只包含Option的子命令')
  .option('-C, --cc <optionC>', 'XXXX数.')
  .option('-D, --dd <optionD>', 'XXXX数.')
  .action(function() {
    execute(doCmd1, arguments);
  });
program
  .command('cmd2 <arg1> <arg2> [arg3]')
  .description('只包含命令行参数的子命令')
  .action(function() {
    execute(doCmd2, arguments);
  }).on('--help', function() {
    console.log('  arg1:');
    console.log('');
    console.log('    arg1的含义说明');
    console.log('');
  });
program
  .command('cmd3 <arg1> <arg2> [arg3]')
  .option('-E, --ee <optionE>', 'XXXX数.')
  .option('-F, --ff <optionF>', 'XXXX数.')
  .description('同时包含命令行参数和Option的子命令')
  .action(function() {
    execute(doCmd3, arguments);
  }).on('--help', function() {
    console.log('  arg1:');
    console.log('');
    console.log('    arg1的含义说明');
    console.log('');
  });
program.parse(process.argv);
var mainFlg = true;
if (!process.argv.slice(2).length) {
  program.outputHelp(make_red);
  mainFlg = false;
}
function make_red(txt) {
  return colors.red(txt); //display the help text in red on the console
}
var cmds = [];
program.commands.map(function(cmd) {
  cmds.push(cmd._name);
});
for (var arg of process.argv.slice(2)) {
  if (cmds.indexOf(arg) >= 0) {
    mainFlg = false;
  }
}
if (mainFlg) {
  execute(doMain, [program]);
}
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 199,830评论 5 468
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 83,992评论 2 376
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 146,875评论 0 331
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 53,837评论 1 271
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 62,734评论 5 360
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,091评论 1 277
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,550评论 3 390
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,217评论 0 254
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,368评论 1 294
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,298评论 2 317
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,350评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,027评论 3 315
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,623评论 3 303
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,706评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 30,940评论 1 255
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,349评论 2 346
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 41,936评论 2 341

推荐阅读更多精彩内容

  • 一种编程语言是否易用,很大程度上,取决于开发命令行程序的能力。 Node.js 作为目前最热门的开发工具之一,怎样...
    猪猪9527阅读 702评论 0 1
  • 1.创建文件夹 !/bin/sh mkdir -m 777 "%%1" 2.创建文件 !/bin/sh touch...
    BigJeffWang阅读 10,011评论 3 53
  • 生命的旅途漫长而久远 生命极其长久 我们对于它来说只不过是匆匆过客 我们只不过是在属于我们的时间 做一些我们该做的...
    寒暄醉乐阅读 155评论 0 0
  • 1.应用层/表示层 超文本传输协议HTTPHTTP是一个客户端终端(用户)和服务器端(网站)请求和应答的标准(TC...
    rogertan30阅读 2,777评论 0 1
  • 刚吃完晚饭的孩子 溜出来消食 掷一把石子儿 到平静的河里 清冷的夜空 瞰了 一汪碎月 ——《碎月》 ​​​​
    段童阅读 183评论 0 2