package.json 中有一個 bin 字段,指定各個內部命令對應的可執行文件的位置。 在包安裝時,若是是全局安裝,npm 將會把 package.json 裏定義的 bin 文件軟鏈接到全局 node_modules/bin,若是是非全局安裝,會軟連接到項目文件夾./node_modules/.bin/。 根據下面代碼的配置,當咱們全局安裝此包後,在任意位置運行 cli-test,都會執行全局 node_modules 中的 cli-test 文件。node
/*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-test 文件,第一行必寫,是告訴Unix和Linux系統這個文件中的代碼用node可執行程序去運行它。 後面就作咱們要作的事情就好了 。json
#!/usr/bin/env node //do something
好了,到這裏咱們的cli就完成了。 其實有不少三方cli也並無用相似 commander 的 node 庫,若是咱們的 cli 足夠簡單,以上這樣就能夠了。 下面接着講 commander。數組
如今咱們先寫一個簡單的文件來理解(也推薦先自行預覽一下 commander 官方文檔),下面是 bin 文件夾的 cli-test,代碼以下:工具
#!/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命令)學習
接着,咱們執行正確的命令參數,以下ui
這樣一個簡單的demo就實現了,看起來也挺簡單的,commander 封裝了一些也不算很複雜的功能。 spa
新建了兩個文件,要以 bin 命令的執行文件命後面加上 -name,做爲子命令文件命令行
cli-test:code
#!/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,要注意下建立的文件類型。