從零開始打造我的專屬命令行工具集——yargs徹底指南

前言

使用命令行程序對程序員來講很常見,就算是前端工程師或者開發gui的,也須要使用命令行來編譯程序或者打包程序前端

熟練使用命令行工具能極大的提升開發效率,linux自帶的命令行工具都很是的有用,可是這些工具都是按照通用需求開發出來的
,若是有一些特別的需求,仍是須要本身寫腳原本完成一些好比文件批量重命名,文件內容批量替換等任務來提供工做效率。node

在node.js出來以前,python常常被用來開發一些腳本完成特殊的任務,好比python爬蟲,python相關的教程有不少,有興趣的本身google。python

得益於node.js的異步io特性,使用node開發io密集類任務變得很是簡單,這篇文章就爲你們講講怎麼使用node.js的yargs模塊來開發本身的命令行工具集合。linux

命令行參數解析

yargs是一個npm模塊用來完成命令行參數解析的,回到使用shell開發命令行的時代,getopts是第一代命令行參數解析工具,通過shell => python => node.js
的迭代,命令行參數解析程序其實沒有多大的進化,它們的目的始終是把用戶從命令行傳入的參數解析成指定的格式,供程序使用git

雖然沒有多大變化,可是因爲開發一個命令行參數解析模塊比較簡單,因此目前node社區存在不少相似yargs的開源項目,這裏簡單列舉一下,有興趣的能夠本身去了解一下,
而後選擇本身喜歡的項目來使用。程序員

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

這裏建議使用第三個接口,這樣能保持模塊的內聚,這種模塊你能掛載在任何命令下面,遷移的時候不須要修改模塊代碼,只須要修改引入模塊的代碼就能實現

.commandDir

若是有大量的命令都使用上面的.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編輯文章,而後發佈,對這一類網站你均可以開發本身的命令行統一進行管理,當你寫完文章以後,只須要一個簡單
的命令,就能把文章同時推送到各大網站

歡迎你們交流本身的想法!

我的博客地址:自由前端

相關文章
相關標籤/搜索