本文先介紹原生的node.js實現命令行交互,瞭解原生的api,而後經過commander.js和inquirer.js實現一個完整的交互命令行工具。
項目地址vue
process對象是一個全局變量,它提供了當前node.js進程的信息並對其控制。由於其是一個全局變量因此無需在文件中引入。node
須要用到的幾個apiwebpack
process.argv屬性返回一個數組。數組的第一個值是process.execPath,第二個是正在執行的JavaScript的文件路徑,其他參數爲其它命令參數,這是咱們來自定義命令的關鍵。git
新建argv.jsgithub
// argv.js console.log(process.argv)
執行node命令 node argv.jsweb
node argv.js --name zhu ## 輸出 [ '/usr/local/bin/node', ## 執行當前腳本的Node二進制文件的絕對路徑 '/Users/zhuhuilong/Node/Book/argv.js', ## 文件的絕對路徑 '--name', ## 其他參數 'zhu' ]
接收自定義的命令參數進行處理輸出shell
// argv.js console.log(process.argv) let argvs = process.argv let param = argvs.splice(2) if(param[0] && param[0] == '--name'){ if(param[1]){ console.log(`hello ${param[1]}`) }else{ console.log('請輸入name') } }
運行argv.jsnpm
node argv.js --name zhu ## 輸出 [ '/usr/local/bin/node', '/Users/zhuhuilong/Node/Book/argv.js', '--name', 'zhu' ] hello zhu param [ '--name', 'zhu' ]
process.stdin(標準輸入)json
process.stdin 屬性返回鏈接到 stdin (fd 0) 的流。 它是一個 net.Socket 流(也就是雙工流),除非 fd 0 指向一個文件,在這種狀況下它是一個可讀流。api
process.stdout(標準輸出)
process.stdout 屬性返回鏈接到 stdout (fd 1) 的流。 它是一個 net.Socket 流(也就是雙工流),除非 fd 1 指向一個文件,在這種狀況下它是一個可寫流。
通俗來說就是控制檯等待咱們輸入內容不退出進程,對輸入輸出進行交互。
新建inputout.js
// inputout.js process.stdin.setEncoding('utf8') let argvs = process.argv let param = argvs.splice(2) if (param[0] && param[0] == '--name') { if (param[1]) { console.log(`hello ${param[1]}`) } else { process.stdout.write(`請輸入name:`) process.stdin.resume() process.stdin.on('data', chunk => { if (!!chunk.replace(/[\r\n]/g, '')) { process.stdout.write(`你輸入的name是: ${chunk}`) process.stdin.emit('end') } else { process.stdout.write(`請輸入name:`) } }) } } process.stdin.on('end', () => { process.stdout.write('結束\n') })
>執行node inputout.js --name
在新版本node模式下可使用process.stdin.on("readable",()=>{})代替process.stdin.resume()恢復輸入流接收。
示例:
process.stdin.on("readable", () => { var chunk = process.stdin.read(); console.log(typeof(chunk)) if (chunk !==null) { process.stdout.write(`data: ${chunk}`); process.stdin.emit("end"); } }); process.stdin.on("end", () => { process.stdout.write("end"); });
從上面的示例咱們能夠拿到process.argv參數對其進行處理交互,但若是要實現更復雜的命令交互,使用上面的方法會很吃力。下面咱們使用commander.js和inquirer來實現一個完整的node命令行工具(建立項目模版)。
node.js命令行界面的完整解決方案,受Ruby Commander啓發。
program.version() 聲明版本
const program = require('commander') const pkg = require('../package.json') program.version(pkg.version)
Options 解析
使用.option()方法定義commander的選項options,也能夠做爲選項的文檔。
var program = require('commander'); program .version('0.1.0') .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);
添加自定義命令program.command()
var program = require('commander'); program .command('rm <dir>') //<>必選參數,若是是[]則是可選參數 .option('-r, --recursive', 'Remove recursively') .action(function (dir, cmd) { console.log('remove ' + dir + (cmd.recursive ? ' recursively' : '')) }) program.parse(process.argv) // command()可變參數 /** 命令command有且只有最後一個參數可變不固定的。 要使參數變量可變,必須將...附加到參數名稱。**/ program .version('0.1.0') .command('rmdir <dir> [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);
program.action() 定義命令的回調函數
var program = require("commander"); program .command("rm <dir>") .option("-r, --recursive", "Remove recursively") .option("-f, --force", "remove force") .action(function(dir, cmd) { // cmd爲option參數選項 //console.log('cmd',cmd) if (cmd.recursive) { console.log("remove " + dir + " recursively"); } if (cmd.force) { console.log("remove " + dir + " forcefully"); } }); program.parse(process.argv);
Inquirer.js使用NodeJs作的一個通用交互式命令行用戶界面的集合。具備經常使用的控制檯交互操做。
因爲交互的問題種類不一樣,inquirer爲每一個問題提供不少參數:
const program = require('commander') const inquirer = require('inquirer') const fs = require('fs') const path = require('path') const pkg = require('../package.json') const CWD = process.cwd() const promptList = [ { type: 'list', message: '請選擇一種模版', name: 'template', choices: ['vue', 'angular', 'webpack-m-pages'], filter: function(val) { return val.toLowerCase() } } ] program .version(pkg.version) .command('create <dir>') .description('create project template') .action(function(dir, cmd) { const TEMPLATE_PATH = path.join(CWD, dir) if (fs.existsSync(TEMPLATE_PATH)) { } else { fs.mkdirSync(TEMPLATE_PATH) } if (dir) { inquirer.prompt(promptList).then(anwsers => { console.log(anwsers) }) } }) program.parse(process.argv)
運行 node cli/cli.js create vue
已經能夠運行了,咱們自定義一個命名替代每次都執行node
命令爲:test-cli create <dir>
一、建立bin文件夾,在bin文件夾下建立index.js文件
#!/usr/bin/env node require('../cli/cli')
二、修改package.json文件
添加bin選項
"bin": { "test-cli": "./bin/index.js" },
三、執行npm link (若是沒有權限,執行sudo npm link)
四、測試
五、發佈 npm publish (若是未登陸需先 npm login登陸)
六、發佈完畢,需npm unlink解除本地的命令映射npm install -g XXX