Commander,教你如何用 Node.js 開發 CLI 工具!

身為一位 CLI(Command Line Interface, CLI) 控,自幹開發用的輔助工具時當然也要自幹一個屬於自己的 CLI 工具啊!不然怎麼對得起那個常駐在下方的 Terminal 呢?不過自幹工具不是本篇重點,今天是要來跟大家講講 Commander 這一個 Node 模組。

Commander 是一個專門解析啟動程式時所輸入的參數的模組。以我們最常輸入的 npm install 這個指令為例子來說明,npm 是指程式名稱,install則是指要程式執行的動作,這個就是所謂的參數,以 C 語言來說的話大概就是 argv 這個放在 main 括號中的東西了。

本教學共分三部分:

  1. 初始設定
  2. 小試身手
  3. 模組介紹

初始設定

在開始進入正題前,我們先來幫我們要做的東西來做點準備工作。

首先建立一個專案資料夾,這資料夾我們就叫…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 的程式完成啦!

Commander 模組介紹

這個模組在許多以 node 開發出來的 CLI 工具中蠻常見的,例如常用來管理/執行的 PM2 就是。

它提供了一些方便的 API 供程式開法者方便使用,設定上也相當直覺,大概就是把一些想要的命令字串敲一敲,然後它就會自動地去幫你分析使用者輸入的參數,並執行你所希望執行的動作。

選項頗析 Option Parsing

這個部分是我們常看到指令後方會有一些類似 -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);

多重輸入參數 Variadic arguments

在參數後面加上 ... 可以把這個參數定義成擁有多重輸入值的參數。範例:

#!/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);

參數定義規則 Specify the argument syntax

#!/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])代表是可選擇性輸入的參數。

自動說明文件 Automated –help

基本的介紹大致就到這裡,若需要更多細節可以到NPM網站上翻看看。

duye.chen

Recent Posts

[教學] 打造你的 NFT 智能合約 – ERC721A

GM!前些日子在幣圈亂玩,一路...

2 年 ago

JavaScript – Singleton 設計模式

前言 在設計程式時,我們有時會...

3 年 ago

PlaidML 讓你的 Mac 也能加速 Tensorflow 機器學習!

相信很多使用 Mac 或者手上...

3 年 ago

RESTful API 測試很煩,只好動手寫屬於自己的測試了

寫在最前面 嗨,大家好久不見!...

3 年 ago

Node.js 與 Socket.io – 即時聊天室實作:資料庫

經過前兩篇(一、二)文章,我們...

6 年 ago