commanderJs編寫命令行工具(cli)

前言:

  最近須要作一個內部的node cli來獨立構建流程,對整個命令行工具實現流程有了大體瞭解,下面來解釋一下如何實現一個cli,和如何使用 commander 庫。node


新手誤區:

  在開始實現以前,我知道有 commander 這個node庫,有不少cli使用了它,對它的大體瞭解就是它能夠幫助開發者簡化實現命令流程。 因而最初覺得不利用庫要去實現命令行會很麻煩,commander 幫咱們解決了各類兼容問題等等。 後來發現並非的,沒有commander 咱們也能夠不用化多大力氣實現命令行,commander 僅僅是一個自己也不太複雜的封裝(源碼也只有1200行)。 咱們的第一步仍是應該先搞清楚,經過npm如何實現命令行。git


一個簡單的cli:

bin:

  package.json 中有一個 bin 字段,指定各個內部命令對應的可執行文件的位置。 在包安裝時,若是是全局安裝,npm 將會把 package.json 裏定義的 bin 文件軟鏈接到全局 node_modules/bin,若是是非全局安裝,會軟連接到項目文件夾./node_modules/.bin/。 根據下面代碼的配置,當咱們全局安裝此包後,在任意位置運行 cli-test,都會執行全局 node_modules 中的 cli-test 文件。github

/*cli-test 的 package.json*/
"bin": { "cli-test": "./bin/cli-test" }
複製代碼

若是咱們把cli安裝在項目A node_modules中,經過設置項目中 package.json 的 scripts,運行 npm run cli,npm 就會在項目的 node_modules/.bin 尋找並運行 cli-test 文件。npm

/*項目A package.json*/
"scripts": { "cli": "cli-test" }
複製代碼


cli文件:

  下面是 cli-test 文件,第一行必寫,是告訴Unix和Linux系統這個文件中的代碼用node可執行程序去運行它。 後面就作咱們要作的事情就好了 。json

#!/usr/bin/env node

//do something
複製代碼

好了,到這裏咱們的cli就完成了。 其實有不少三方cli也並無用相似 commander 的 node 庫,若是咱們的 cli 足夠簡單,以上這樣就能夠了。 下面接着講 commander。數組


commander:

如今咱們先寫一個簡單的文件來理解(也推薦先自行預覽一下 commander 官方文檔),下面是 bin 文件夾的 cli-test,代碼以下:bash

#!/usr/bin/env node
 const program =require('commander');
program
.usage('\[option\]', '--type required')
.option('--type \[typeName\]', 'type: dev && build')
.parse(process.argv);

const {type} = program; if(type == 'dev'){
    console.log('do something', type)
}else if(type == 'build'){
    console.log('do something', type)
}else{
    console.log('params error');
    program.help();
}

複製代碼


解釋一下上面的代碼,從查看源碼裏發現 require('commander') 會 new一個commander 內部的單例對象並返回,program 已是一個實例,。 .usage 僅僅描述了參數規則,會在 --help 中打印出來。.option 定義了一個參數名和描述, parse 會解析命令之中的參數,根據上面定義好的規則執行相關命令。 好比上面的代碼定義了 option 類型的參數 --type,執行 .parse 的時候,parse 根據 process.argv 之中的參數,獲取到 --type,並把參數命和參數值存儲在內部 commander 實例的屬性之中,所以後面的代碼就能從 program 之中取到 type,若是 type 不存在或者不是咱們約定的值,最後咱們打印參數錯誤,並執行help方法打印了 --help。 以下截圖,咱們 node 執行 cli-test,由於沒有約定參數,因此執行了 else 的程序。(由於這裏是本地的demo程序,因此直接使用node命令)工具


接着,咱們執行正確的命令參數,以下學習


這樣一個簡單的demo就實現了,看起來也挺簡單的,commander 封裝了一些也不算很複雜的功能。 ui


再來一個例子:

新建了兩個文件,要以 bin 命令的執行文件命後面加上 -name,做爲子命令文件


cli-test:

#!/usr/bin/env node
 const program =require('commander');

program .usage('<command> \[option\]', 'option --type required')
.command('h5', 'to h5')
.command('rn', 'to rn')
.parse(process.argv);
複製代碼


cli-test-h5:

#!/usr/bin/env node
 const program =require('commander');
program
.option('--type \[typeName\]', 'type: dev && build')
.parse(process.argv);

const {type} = program; if(type == 'dev'){
    console.log('do something h5', type)
}else if(type == 'build'){
    console.log('do something h5', type)
}else{
    console.log('params error');
    program.help();
}

複製代碼


cli-test-rn:

#!/usr/bin/env node
 const program =require('commander');
program
.option('--type \[typeName\]', 'type: dev && build')
.parse(process.argv);

const {type} = program; if(type == 'dev'){
    console.log('do something rn', type)
}else if(type == 'build'){
    console.log('do something rn', type)
}else{
    console.log('params error');
    program.help();
}

複製代碼


先直接運行3個命令運行程序,看下結果,然後分別解釋一下:



node ./bin/cli-test:

定義了.command子命令卻沒有相應執行參數,commander對象會直接打印-help,並process.exit退出進程。


node ./bin/cli-test h5 --type dev:

cli-test 經過 command 方法約定子命令名稱和描述,如 h5,當執行 node cli-test h5 --type dev的時候,cli-test 執行到 .command('h5', 'to h5') ,會在當前 commander 實例內部,new 一個 name 爲 h5 的子 commander,存儲在當前父實例的 commands 數組中,當 .parse(process.argv) 執行,獲取到參數中 h5 後,在 commands 裏查找是否有 name 爲 h5 的 commander 子實例,若是查找到,啓動一個子進程按照命名規則執行 cli-test-h5 文件並帶入後面的 option 參數。 這樣 commander 就幫助咱們實現了多文件命令劃分,咱們能夠把不一樣類型的執行代碼放在不一樣的文件中。


node ./bin/cli-test rn --type build:

同上


小結

bin 文件夾下的這3個 node 文件他們都是 commander 實例,commander 庫只是一個簡單的封裝,幫助定義 多文件命令、執行參數 、簡易文檔,參數驗證等。 以上就是 commander 的大體使用和我對其的理解。 源碼很少,建議能夠深刻學習一下。

最後

  到最後你們結合實現以上所說的 cli 和 commander,一個 commander 實現的命令行工具就能完成了,是否是很簡單!?

注意

  若是執行命令發現報錯爲 error: xx(1) not executable. try chmod or run with root,要注意下建立的文件類型。

相關文章
相關標籤/搜索