從零開始打造個人專屬命令列工具集——yargs 完全指南
前言
使用命令列程式對程式設計師來說很常見,就算是前端工程師或者開發 GUI 的,也需要使用命令列來編譯程式或者打包程式。
熟練使用命令列工具能極大的提高開發效率,Linux 自帶的命令列工具都非常的有用,但是這些工具都是按照通用需求開發出來的,如果有一些特別的需求,還是需要自己寫指令碼來完成一些比如檔案批量重新命名,檔案內容批量替換等任務來提供工作效率。
在 Node.js 出來之前,Python 經常被用來開發一些指令碼完成特殊的任務,比如 Python 爬蟲,Python 相關的教程有很多,有興趣的自己 Google。
得益於 Node.js 的非同步 io 特性,使用 Node.js 開發 io 密集類任務變得非常簡單,這篇文章就為大家講講怎麼使用 Node.js 的 yargs 模組來開發自己的命令列工具集合。
命令列引數解析
yargs 是一個用來完成命令列引數解析的 npm 模組,回到使用 shell 開發命令列的時代,getopts 是第一代命令列引數解析工具,經過shell => python => node.js
的迭代,命令列引數解析程式其實沒有多大的進化,它們的目的始終是把使用者從命令列傳入的引數解析成指定的格式,供程式使用。
雖然沒有多大變化,但是由於開發一個命令列引數解析模組比較簡單,所以目前 Node.js 社群存在很多類似 yargs 的開源專案,這裡簡單列舉一下,有興趣的可以自己去了解一下, 然後選擇自己喜歡的專案來使用。
-
optimist 模仿 Python 的 optimist 專案
-
commander.js tj 是 Node.js 大神,co 的作者, commander.js 源自 ruby 的 commander 專案,作者也是 tj
-
nomnom 不再維護,不建議使用
yargs
讀過阮一峰的 Node.js 命令列程式開發教程之後開始使用 yargs 開發自己命令列工具, 用過一段時間發現非常的好用。
自阮大神的文章釋出以來,yargs 有了一些改動,新增有很多有用的功能,特別是.commandDir(directory, [opts])
這個功能,對打造命令列工具集合非常有用,所以寫一個新版本的 yargs 教程還是有必要的。
yargs 的用法還算比較簡單,對英文有自信的可以去首頁閱讀原版:yargs。
簡單模式
yargs 預設使用兩個--
作為引數的字首,引數名與引數值中間使用空格或者=
分隔都可以。
下面的程式碼展示了 yargs 最簡單的用法,你只需要引入 yargs,就能讀取命令列引數,不需要寫任何的配置,非常的簡單。
#!/usr/bin/env node
var argv = require('yargs').argv;
if (argv.ships > 3 && argv.distance < 53.5) {
console.log('Plunder more riffiwobbles!');
} else {
console.log('Retreat from the xupptumblers!');
}
$ ./plunder.js --ships=4 --distance=22
Plunder more riffiwobbles!
$ ./plunder.js --ships 12 --distance 98.7
Retreat from the xupptumblers!
示例程式碼都來自官網:yargs
簡單模式還能讀取短變數如-x 4
相當於argv.x = 4
。簡單模式還能讀取布林型別-s
相當於argv.s = true
。簡單模式還能讀取非-
開始的變數,這種型別的變數儲存在argv._
陣列裡面。
引數配置
簡單模式的功能都只用一行程式碼就能實現:
var argv = require('yargs').argv;
但是如果你想統計變數出現的次數怎麼辦? 答案就是新增引數配置選項。
#!/usr/bin/env node
var argv = require('yargs')
.count('verbose')
.alias('v', 'verbose')
.argv;
VERBOSE_LEVEL = argv.verbose;
function WARN() { VERBOSE_LEVEL >= 0 && console.log.apply(console, arguments); }
function INFO() { VERBOSE_LEVEL >= 1 && console.log.apply(console, arguments); }
function DEBUG() { VERBOSE_LEVEL >= 2 && console.log.apply(console, arguments); }
WARN("Showing only important stuff");
INFO("Showing semi-important stuff too");
DEBUG("Extra chatty mode");
上面的程式能統計verbose
引數出現的次數,縮寫-v
也會統計進去,具體呼叫例子參考下面的程式碼:
$ node count.js
Showing only important stuff
$ node count.js -v
Showing only important stuff
Showing semi-important stuff too
$ node count.js -vv
Showing only important stuff
Showing semi-important stuff too
Extra chatty mode
$ node count.js -v --verbose
Showing only important stuff
Showing semi-important stuff too
Extra chatty mode
yargs 提供很多介面用來幫助完善命令列程式:
var argv = require('yargs')
.usage('Usage: $0 -w [num] -h [num]')
.argv;
#!/usr/bin/env node
var argv = require('yargs')
.usage('Usage: $0 -w [num] -h [num]')
.demand(['w','h'])
.argv;
#!/usr/bin/env node
var argv = require('yargs')
.default('x', 10)
.default('y', 10)
.argv
;
console.log(argv.x + argv.y);
#!/usr/bin/env node
var argv = require('yargs')
.usage('Usage: $0 <command> [options]')
.help('h')
.alias('h', 'help')
.epilog('copyright 2015')
.argv;
var argv = require('yargs')
.usage('Usage: $0 <command> [options]')
.alias('h', 'help')
.argv;
訪問argv.h
相當於訪問argv.help
。
引數陣列:
var argv = require('yargs')
.usage('Usage: $0 <command> [options]')
.alias('n', 'name')
.array('n')
.argv;
console.log(argv.n);
呼叫:
node array_test.js -n abc test
var argv = require('yargs')
.alias('i', 'ingredient')
.describe('i', 'choose your sandwich ingredients')
.choices('i', ['peanut-butter', 'jelly', 'banana', 'pickles'])
.help('help')
.argv
上述程式碼設定argv.i
的值只能是['peanut-butter', 'jelly', 'banana', 'pickles']
陣列中的一個。
上面是yargs比較簡單的用法,如果想閱讀完整版,建議去github上閱讀。
子命令
yargs 適合開發複雜的命令列程式的另一個原因是它支援子命令,而且子命令可以巢狀,這意味著你也可以開發出類似 git 這樣擁有上百個命令的程式
yargs 的子命令有兩種模式:.command(*)
和.commandDir(directory, [opts])
。
.command
.command
方法有三個介面:
.command(cmd, desc, [builder], [handler])
.command(cmd, desc, [module])
.command(module)
其實它們的用法都差不多,可以把它們都看作傳遞一個 module 給 yargs,這個 module 必須匯出四個變數cmd, desc [builder], [handler]
,其中 builder 和 handler 是方法,另外兩個是字串。
使用第一個介面的示例:
yargs
.command(
'get',
'make a get HTTP request',
function (yargs) {
return yargs.option('u', {
alias: 'url',
describe: 'the URL to make an HTTP request to'
})
},
function (argv) {
console.log(argv.url)
}
)
.help()
.argv
使用第三個介面需要把這個模組在單獨的檔案,然後用 require 引入。
這是模組的程式碼:
// my-module.js
exports.command = 'get <source> [proxy]'
exports.describe = 'make a get HTTP request'
exports.builder = {
banana: {
default: 'cool'
},
batman: {
default: 'sad'
}
}
exports.handler = function (argv) {
// do something with argv.
}
引入的時候這樣使用:
yargs.command(require('my-module'))
.help()
.argv
當額外的模組沒有定義 cmd 和 desc 的時候可以使用第二個介面:
yargs.command('get <source> [proxy]', 'make a get HTTP request', require('my-module'))
.help()
.argv
這裡建議使用第三個介面,這樣能保持模組的內聚,這種模組你能掛載在任何命令下面,遷移的時候不需要修改模組程式碼,只需要修改引入模組的程式碼就能實現
如果有大量的命令都使用上面的.command(module)
來開發的話,這些模組都有相同的結構,應該能有方法簡化這些命令的引入過程,把這個過程自動化,基於 這個目的 yargs 提供了.commandDir
介面
下面參考一個我自己寫的專案 pit。
下面是這個專案的目錄結構:
.
├── pit
│ ├── douban
│ │ └── movie.js
│ ├── douban.js
│ ├── gg
│ │ ├── client.js
│ │ ├── login.js
│ │ ├── scope.js
│ │ ├── scope.json
│ │ ├── secret.json
│ │ ├── token.json
│ │ └── upload.js
│ ├── gg.js
│ ├── git
│ │ ├── commit.js
│ │ ├── create.js
│ │ ├── deploy.js
│ │ ├── push.js
│ │ └── token.json
│ ├── git.js
│ ├── gm.js
│ ├── md5.js
│ ├── news
│ │ ├── bing.js
│ │ ├── funs.js
│ │ ├── funs.json
│ │ ├── games.js
│ │ ├── games.json
│ │ ├── google.js
│ │ ├── newsall.json
│ │ ├── shops.js
│ │ ├── shops.json
│ │ ├── videos.js
│ │ └── videos.json
│ └── news.js
└── pit.js
pit.js:命令列的入口
#!/usr/bin/env node
require('yargs')
.commandDir('pit')
.demand(1)
.help()
.locale('en')
.showHelpOnFail(true, 'Specify --help for available options')
.argv
這段程式碼只指定讀取同目錄下同名資料夾pit
下面的命令載入為子命令。
注意:commandDir 預設只會載入目錄下第一級的檔案,不會遞迴載入,如果想遞迴載入需要這樣寫
.commandDir('pit', {recurse: true})
接著來看 git 子命令,因為 git 專案每次提交都要重複幾個相同的步驟,所有想開發一個更簡單的命令進行打包提交。
git.js
exports.command = 'git <command>';
exports.desc = 'github command list';
exports.builder = function (yargs) {
return yargs.commandDir('git')
}
exports.handler = function (argv) {}
git 也是載入一個目錄作為自己的子命令:以 commit 為例
commit.js
'use strict';
var fs = require('fs');
var path = require('path');
require('shelljs/global');
var Q = require('q');
function _exec(cmd) {
var deferred = Q.defer();
exec(cmd, function (code, stdout, stderr) {
deferred.resolve();
});
return deferred.promise;
}
exports.command = 'commit';
exports.desc = 'commit repo local';
exports.builder = function (yargs) {
return yargs
.help('h');
};
exports.handler = function (argv) {
var repo = process.cwd();
var name = path.basename(repo);
Q.fcall(function () { })
.then(() => _exec(`git add .`))
.then(() => _exec(`git commit -m 'd'`))
.catch(function (err) {
console.log(err);
})
.done(() => {
console.log(`commit ${repo} done`);
});
}
這個命令預設執行在 git 專案的根目錄,和 git 命令不太一樣,git 可以在專案根目錄下的任意子目錄裡面執行。
使用 shelljs 來執行子命令,然後用 Q 進行 promise 封裝,保證命令的執行順序,同時把命令列輸出和錯誤資訊都列印到控制。
一個很簡單能節省時間的命令列程式,作為拋磚引玉之用。
延伸
高手都是擅長使用命令列(電影裡面的高手也一樣),當你習慣使用命令列完成日常任務之後,慢慢的會形成一種依賴。繼續下去,你會考慮把所有的事情都用來命令列來完成,當然這個 目的不能實現,因為能自動完成所有任務的命令列不叫命令列——它叫 AI。
雖然不能開發一臺高智慧 AI,但是還是有很多工能用命令列來完成的,這裡寫下我的思路,供大家參考。
api 命令列
大型網站都提供自己的 api 介面配上 oauth2.0 認證,如果你想使用命令列來呼叫這些 api 介面,你完全可以做到。
像 aws,google cloud,aliyun 這種雲主機,使用命令列能節省很多運維的時間。
另外你也可以參考上面 pit.js 寫的 douban.js 來抓取豆瓣的資料,豆瓣的公共 api 不需要認證就能訪問,用來做一些測試非常方便。
命令列爬蟲
使用 Node.js 開發爬蟲就像使用 Python 一樣簡單,但是一個功能齊全的爬蟲必然少不了命令列介面,你不可能每次有新的需求都來修改程式碼,下次再給大家分享我寫的一個簡單的基於 Node.js 的爬蟲專案。
表單提交
對一些不提供 api 介面但是又想使用命令來進行互動的網站,你可以使用表單提交來進行登入,然後做一些登入之後才能做的事情:例如發表文章。
現在很多的網站都支援使用 markdown 編輯文章,然後釋出,對這一類網站你都可以開發自己的命令列統一進行管理,當你寫完文章之後,只需要一個簡單 的命令,就能把文章同時推送到各大網站。
歡迎大家交流自己的想法!
相關文章
- 從零開始打造專屬釘釘機器人機器人
- 從零開始,打造屬於你的 ChatGPT 機器人!ChatGPT機器人
- 從零開始的 Jupyter 雲伺服器完全搭建指南伺服器
- 從今天開始,拿起VuePress打造屬於自己的專屬部落格Vue
- 從零開發一個node命令列工具命令列
- vuePress從零開始搭建自己專屬的文件集合Vue
- 從零開始:Django專案的建立與配置指南Django
- 打造DiTing聊天室之從零開始:準備工作指南
- 從零開始的 Android 新專案 - 收藏集 - 掘金Android
- 從零開始的個人技術部落格
- 從零開始打造 Mock 平臺 - 核心篇Mock
- 從零開始開發一個Node互動式命令列應用命令列
- 從零開始打造 Mock 平臺 - 功能模組篇Mock
- 從零開始使用 Astro 的實用指南AST
- 從零開始學mitmproxy抓包工具MIT
- 從零開始搭建屬於自己的網站網站
- 從零開始的Unity個人學習日記(二)Unity
- 從零開始打造自己的PHP框架――第2章PHP框架
- 從零開始-打造一個JavaScript完整線上教程文件JavaScript
- 從零開始:TensorFlow機器學習模型快速部署指南機器學習模型
- 從零開始的Python爬蟲速成指南Python爬蟲
- 從零開始打造流程圖、拓撲圖專案【Nuxt.js + Element + Vuex】流程圖UXJSVue
- 從零開始製作cli工具,快速建立專案腳手架
- 教你一鍵製作專屬的活動邀請函!從零開始教程!
- Python從零開始——學習Python的個人方法Python
- 從零開始的爬蟲專案(一)爬蟲
- 從零開始React專案架構(六)React架構
- 從零開始React專案架構(三)React架構
- 從零開始React專案架構(四)React架構
- 從零開始React專案架構(五)React架構
- 從零開始搭建一個vue專案Vue
- 從零開始React專案架構(一)React架構
- 從零開始React專案架構(二)React架構
- 從零開始搭建vue.js專案Vue.js
- 從零開始機器學習機器學習
- 從零開始 OpenCVOpenCV
- Git 詳細的操作指南筆記(從零開始)Git筆記
- 全面掌握 Jest:從零開始的測試指南(上篇)