身為一位 CLI(Command Line Interface, CLI) 控,自幹開發用的輔助工具時當然也要自幹一個屬於自己的 CLI 工具啊!不然怎麼對得起那個常駐在下方的 Terminal 呢?不過自幹工具不是本篇重點,今天是要來跟大家講講 Commander 這一個 Node 模組。
Commander 是一個專門解析啟動程式時所輸入的參數的模組。以我們最常輸入的 npm install
這個指令為例子來說明,npm
是指程式名稱,install
則是指要程式執行的動作,這個就是所謂的參數,以 C 語言來說的話大概就是 argv
這個放在 main 括號中的東西了。
本教學共分三部分:
在開始進入正題前,我們先來幫我們要做的東西來做點準備工作。
首先建立一個專案資料夾,這資料夾我們就叫…s9cli好啦!建立好後,在資料夾中建立一個 js 檔案,我想我們就命名為 index.js
。
再來是初始化整個專案為 npm 可識别的專案。
npm init
這個指令是一個互動式指令,他會詢問各項目的值,此例除了一開始的名稱定義外,我們大多數使用預設的即可,也就是一直按 Enter 案到底 XD
這個 name 等於是你 CLI 工具的程式名稱
然後安裝所需的模組,在本篇教學中我們只需要安裝 Commander 即可。
npm install commander --save
安裝完成後,我們就把初始化好我們的專案囉。
在這一節中,我會稍加介紹 Commander 的用法,大多數內容取自官方的 README 檔案,有興趣可以前往閱讀原文。
第一步
因為我們要讓系統知道這支程式要能在系統層面執行,所以打開你的 index.js ,我們要在他的最上面加入這行:
#!/usr/bin/env node
然後打開 package.json
並加入
..., "bin": { "s9cli": "./index.js" }, "author": "Duye", ...
搞定!
第二步
載入模組,準備開始寫程式囉!
const program = require('commander');
第三步
在 index.js
中輸入以下程式碼:
#!/usr/bin/env node const program = require('commander'); program .version('0.0.1') .usage('[options]') .option('-n, --name ', 'Your name.') .parse(process.argv); // program.args < 0 代表沒有任何輸入 if (program.args.length < 1) { program.outputHelp(); // 輸出說明 process.exit(); // 關閉程式 } console.log('Hello, %s.', (program.name || 'World'));
然後在這個專案資料夾中執行
npm link
稍微解釋一下這個指令動作幹了什麼事。
當你執行了 npm link
後,npm 會幫這個專案資料夾在 npm 放全域模組的資料夾({prefix}/lib/node_modules/
)中置入一個捷徑(symlink),同時也會將每個 bin 執行檔建立一個捷徑到 {prefix}/bin/{name}
。如此一來,我們就可以比較好開發我們的 CLI 工具,而無需真的部屬或安裝到 npm 的管理系統中。(官網參考)
好的,以上動作完成後,我們嘗試執行看看這支程式看看結果如何。
c9cli -n Duye // output // Hello, Duye.
嘿!一個用 Commander Line 的程式完成啦!
這個模組在許多以 node 開發出來的 CLI 工具中蠻常見的,例如常用來管理/執行的 PM2 就是。
它提供了一些方便的 API 供程式開法者方便使用,設定上也相當直覺,大概就是把一些想要的命令字串敲一敲,然後它就會自動地去幫你分析使用者輸入的參數,並執行你所希望執行的動作。
這個部分是我們常看到指令後方會有一些類似 -o
或 --options
的附加參數,稱為 option,可有可無,就是一個選項這樣。這部分在程式裡面我們用 .option()
來定義。下面這個範例它會去解析來自於 program.argv
內的參數值。
#!/usr/bin/env node /** * Module dependencies. */ var program = require('commander'); program .version('0.0.1') .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);
在參數後面加上 ...
可以把這個參數定義成擁有多重輸入值的參數。範例:
#!/usr/bin/env node
/**
* Module dependencies.
*/
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.forEach(function (oDir) {
console.log('rmdir %s', oDir);
});
}
});
program.parse(process.argv);
#!/usr/bin/env node
var program = require('commander');
program
.version('0.0.1')
.arguments('<cmd> [env]')
.action(function (cmd, env) {
cmdValue = cmd;
envValue = env;
});
program.parse(process.argv);
if (typeof cmdValue === 'undefined') {
console.error('no command given!');
process.exit(1);
}
console.log('command:', cmdValue);
console.log('environment:', envValue || "no environment given");
角括號(如:<cmd>
)代表這個參數是必要參數,中括號(如:[env]
)代表是可選擇性輸入的參數。
基本的介紹大致就到這裡,若需要更多細節可以到NPM網站上翻看看。