最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Node.js 中如何收集和解析命令行參數(shù)

 更新時(shí)間:2021年01月08日 10:54:05   作者:descire  
這篇文章主要介紹了Node.js 中如何收集和解析命令行參數(shù),文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

前言

在開發(fā) CLI(Command Line Interface)工具的業(yè)務(wù)場(chǎng)景下,離不開命令行參數(shù)的收集和解析。

接下來,本文介紹如何收集和解析命令行參數(shù)。

收集命令行參數(shù)

在 Node.js 中,可以通過 process.argv 屬性收集進(jìn)程被啟動(dòng)時(shí)傳入的命令行參數(shù):

 // ./example/demo.js
 process.argv.slice(2);

 // 命令行執(zhí)行如下命令
 node ./example/demo.js --name=xiaoming --age=20 man

 // 得到的結(jié)果
 [ '--name=xiaoming', '--age=20', 'man' ]

由上述示例可以發(fā)現(xiàn),Node.js 在處理命令行參數(shù)時(shí),只是簡(jiǎn)單地通過空格來分割字符串。

對(duì)于這樣的參數(shù)數(shù)組,無法很方便地獲取到每個(gè)參數(shù)對(duì)應(yīng)的值,所以需要再進(jìn)行一次解析操作。

命令行參數(shù)風(fēng)格

在解析命令行參數(shù)之前,需要了解一些常見的命令行參數(shù)風(fēng)格:

  • Unix 風(fēng)格:參數(shù)以「-」(連字符)開頭
  • GNU 風(fēng)格:參數(shù)以「--」(雙連字符)開頭
  • BSD 風(fēng)格:參數(shù)以空格分割

Unix 參數(shù)風(fēng)格有一個(gè)特殊的注意事項(xiàng):「「-」后面緊鄰的每一個(gè)字母都表示一個(gè)參數(shù)名」。

ls -al

上述命令用來顯示當(dāng)前目錄下所有的文件、文件夾并且顯示它們的詳細(xì)信息,等同于:

ls -a -l

GNU 風(fēng)格的參數(shù)以 「--」開頭,一般后面會(huì)跟上一個(gè)單詞或者短語,例如熟悉的 npm 安裝依賴的命令:

 npm install --save koa

對(duì)于兩個(gè)單詞的情況,在 GNU 參數(shù)風(fēng)格中,會(huì)通過「-」來連接,例如 npm 安裝僅用于開發(fā)環(huán)境的依賴:

npm install --save-dev webpack

BSD 是加州大學(xué)伯克利分校開發(fā)的一個(gè) Unix 版本。其與 Unix 的區(qū)別主要在于參數(shù)前面沒有 「-」,個(gè)人感覺這樣很難區(qū)別參數(shù)和參數(shù)值。

注意事項(xiàng):-- 后面緊鄰空格時(shí),表示后面的字符串不需要解析。

解析命令行參數(shù)

function parse(args = []) {
 // _ 屬性用來保留不需要處理的參數(shù)字符串
 const output = { _: [] };

 for (let index = 0; index < args.length; index++) {
  const arg = args[index];
  
  if (isIgnoreFollowingParameters(output, args, index, arg)) {
   break;
  }
  
  if (!isParameter(arg)) {
   output._.push(arg);
   continue;
  }

  ...
 }

 return output;
}

parse(process.argv.slice(2));

接收到命令行參數(shù)數(shù)組之后,需要遍歷數(shù)組,處理每一個(gè)參數(shù)字符串。

isIgnoreFollowingParameters 方法主要用來判斷單個(gè)「--」的場(chǎng)景,后續(xù)的參數(shù)字符串不再需要處理:

function isIgnoreFollowingParameters(output, args, index, arg) {
 if (arg !== '--') {
  return false;
 }
 output._ = output._.concat(args.slice(++index));
 return true;
}

接下來,如果參數(shù)字符串不以「-」開頭,同樣也不需要處理,參數(shù)的形式以 Unix 和 GNU 風(fēng)格為主:

function isParameter(arg) {
 return arg.startsWith('-');
}

參數(shù)的表現(xiàn)形式主要分為以下幾種:

  • "--name=xiaoming": 參數(shù)名為 name,參數(shù)值為 xiaoming
  • "-abc=10": 參數(shù)名為 a,參數(shù)值為 true;參數(shù)名為 b,參數(shù)值為 true;參數(shù)名為 c,參數(shù)值為 10
  • "--save-dev": 參數(shù)名為 save-dev,參數(shù)值為 true
  • "--age 20":參數(shù)名為 age,參數(shù)值為 20
 let hyphensIndex;
 for (hyphensIndex = 0; hyphensIndex < arg.length; hyphensIndex++) {
  if (arg.charCodeAt(hyphensIndex) !== 45) {
   break;
  }
 }

 let assignmentIndex;
 for (assignmentIndex = hyphensIndex + 1; assignmentIndex < arg.length; assignmentIndex++) {
  if (arg[assignmentIndex].charCodeAt(0) === 61) {
   break;
  }
 }

利用 Unicode 碼點(diǎn)值找出連字符和等號(hào)的下標(biāo)值,從而根據(jù)下標(biāo)分割出參數(shù)名和參數(shù)值:

 const name = arg.substring(hyphensIndex, assignmentIndex);

 let value;
 const assignmentValue = arg.substring(++assignmentIndex);

處理參數(shù)值時(shí),需要考慮參數(shù)賦值的四種場(chǎng)景:

 if (assignmentValue) {
  value = assignmentValue; // --name=xiaoming or -abc=10
 } else if (index + 1 === args.length) {
  value = true; // --save-dev
 } else if (('' + args[index + 1]).charCodeAt(0) !== 45) {
  value = args[++index]; // --age 20
 } else {
  value = true; // 缺省情況
 }

由于 Unix 風(fēng)格中每一個(gè)字母都代表一個(gè)參數(shù),并且「手動(dòng)傳遞的參數(shù)值應(yīng)該賦值給最后一個(gè)參數(shù)」,所以還需針對(duì)該場(chǎng)景進(jìn)行適配:

 // 「-」or「--」
 const arr = hyphensIndex === 2 ? [name] : name;
 for (let keyIndex = 0; keyIndex < arr.length; keyIndex++) {
  const _key = arr[keyIndex];
  const _value = keyIndex + 1 < arr.length || value;
  handleKeyValue(output, _key, _value);
 }

最后針對(duì)參數(shù)的賦值操作,需要考慮到「多次賦值」的情況:

function handleKeyValue(output, key, value) {
 const oldValue = output[key];
 if (Array.isArray(oldValue)) {
  output[key] = oldValue.concat(value);
  return;
 }

 if (oldValue) {
  output[key] = [oldValue, value];
  return;
 }

 output[key] = value;
}

到此,命令行參數(shù)的解析功能就完成了,上述方法執(zhí)行的效果如下:

 # 命令行執(zhí)行
 node ./example/step1.js --name=xiaoming --age 20 --save-dev -abc=10 -c=20 -- --ignore

 # 解析結(jié)果
 {
  _: [ '--ignore' ],
  name: 'xiaoming',
  age: '20',
  'save-dev': true,
  a: true,
  b: true,
  c: [ '10', '20' ]
 }

別名機(jī)制

比較優(yōu)秀的 CLI 工具在參數(shù)的解析上都支持參數(shù)的別名設(shè)置,例如使用 npm 安裝開發(fā)環(huán)境依賴時(shí),你可以選擇這種完整的寫法:

npm install --save-dev webpack

你也可以使用下面這種別名方式:

npm install -D webpack

從使用上來說 -D 和 --save-dev 是兩種方式,但是從 CLI 工具的開發(fā)者來說,最終處理邏輯時(shí)只能以一個(gè)參數(shù)名為標(biāo)準(zhǔn),所以對(duì)于一個(gè)命令行參數(shù)解析庫(kù)來說,其結(jié)果需要包含所有的情況:

npm install --save-dev webpack
# 解析的結(jié)果
{ 'save-dev': true, 'D': true }

以上文的解析方法為例,需要添加額外的選項(xiàng)參數(shù),加入 alias 屬性來聲明別名屬性的對(duì)應(yīng)關(guān)系:

 parse(process.argv.slice(2), {
  alias: {
   'save-dev': 'S'
  }
 })

上述方式符合正常的理解:設(shè)置參數(shù)對(duì)應(yīng)的別名。但這是一個(gè)「單向查找關(guān)系」,需要轉(zhuǎn)化為:

 "alias": {
  "save-dev": ["s"],
  "s": ["save-dev"]
 }

因?yàn)閷?duì)于使用者來說,只會(huì)選擇一種方式傳遞參數(shù)。對(duì)于開發(fā)者的話需要根據(jù)任意一個(gè)別名找到其相關(guān)聯(lián)的別名:

function parse(args = [], options = {}) {
 const output = { _: [] };

 const { alias } = options;

 const hasAlias = alias !== void 666;

 if (hasAlias) {
  Object.keys(alias).forEach(key => {
   alias[key] = toArr(alias[key]);
   alias[key].forEach((item, index) => {
    (alias[item] = alias[key].concat(key)).splice(index, 1);
   })
  })
 }

 // 省略解析代碼
 ...

 if (hasAlias) {
  Object.keys(output).forEach(key => {
   const arr = alias[key] || [];
   arr.forEach(sub => output[sub] = output[key])
  })
 }

 return output;
}

除了別名之外,還可以在參數(shù)解析之后做如下優(yōu)化:

  • 參數(shù)值的類型約束
  • 參數(shù)的默認(rèn)值設(shè)定

成熟的解析庫(kù)

針對(duì)一些成熟的命令行參數(shù)解析庫(kù)可以采用基準(zhǔn)測(cè)試查看它們的解析效率:

const nopt = require('nopt');
const mri = require('mri');
const yargs = require('yargs-parser');
const minimist = require('minimist');
const { Suite } = require('benchmark');

const bench = new Suite();
const args = ['--name=xiaoming', '-abc', '10', '--save-dev', '--age', '20'];

bench
 .add('minimist   ', () => minimist(args))
 .add('mri     ', () => mri(args))
 .add('nopt     ', () => nopt(args))
 .add('yargs-parser ', () => yargs(args))
 .on('cycle', e => console.log(String(e.target)))
 .run();

本文的內(nèi)容主要參考解析效率最高的 mri 庫(kù)的源碼,感興趣的同學(xué)可以學(xué)習(xí)其源碼實(shí)現(xiàn)。(順便吐槽一下:嵌套三元操作符可讀性真的很差。。)

雖然上述基準(zhǔn)測(cè)試中 minimist 效率并不很好,但是其覆蓋了比較全的參數(shù)輸入場(chǎng)景。(以上測(cè)試用例覆蓋的場(chǎng)景有限)

到此這篇關(guān)于Node.js 中如何收集和解析命令行參數(shù)的文章就介紹到這了,更多相關(guān)Node.js 解析命令行參數(shù)內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • 使用node.js實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)進(jìn)行CRUD操作

    使用node.js實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)進(jìn)行CRUD操作

    這篇文章主要為大家詳細(xì)介紹了如何使用node.js實(shí)現(xiàn)對(duì)數(shù)據(jù)庫(kù)進(jìn)行CRUD(增刪改查)操作,文中的示例代碼講解詳細(xì),感興趣的小伙伴可以參考一下
    2024-12-12
  • node以及npm版本不對(duì)應(yīng)出錯(cuò)的完美解決方法

    node以及npm版本不對(duì)應(yīng)出錯(cuò)的完美解決方法

    最近項(xiàng)目用到了node和npm,查看一下當(dāng)前版本,發(fā)現(xiàn)有報(bào)錯(cuò),下面這篇文章主要給大家介紹了關(guān)于node以及npm版本不對(duì)應(yīng)出錯(cuò)的完美解決方法,文中通過圖文介紹的非常詳細(xì),需要的朋友可以參考下
    2023-05-05
  • Node.js使用gm拼裝sprite圖片

    Node.js使用gm拼裝sprite圖片

    這篇文章主要介紹了Node.js使用gm拼裝sprite圖片的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • Node.js pipe實(shí)現(xiàn)源碼解析

    Node.js pipe實(shí)現(xiàn)源碼解析

    這篇文章主要介紹了Node.js pipe實(shí)現(xiàn)源碼解析,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2017-08-08
  • 詳解Node.js開發(fā)中的express-session

    詳解Node.js開發(fā)中的express-session

    express-session?是基于express框?qū)iT用于處理session的中間件,本篇文章主要介紹了詳解Node.js開發(fā)中的express-session,有興趣的可以了解一下<BR>
    2017-05-05
  • node.js操作mongodb簡(jiǎn)單示例分享

    node.js操作mongodb簡(jiǎn)單示例分享

    MongoDB是基于Javascript語言的數(shù)據(jù)庫(kù),存儲(chǔ)格式是JSON,而Node也是基于JavaScript的環(huán)境(庫(kù)),所以node和mongoDB的搭配能減少因?yàn)閿?shù)據(jù)轉(zhuǎn)換帶來的時(shí)間空間開銷。今天我們來看看如何通過node.js來操作MongoliaDB
    2017-05-05
  • 用Node提供靜態(tài)文件服務(wù)的方法

    用Node提供靜態(tài)文件服務(wù)的方法

    這篇文章主要介紹了用Node提供靜態(tài)文件服務(wù)的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-07-07
  • nodejs初步體驗(yàn)篇

    nodejs初步體驗(yàn)篇

    這篇文章主要介紹了nodejs初步體驗(yàn)篇的相關(guān)資料,需要的朋友可以參考下
    2015-11-11
  • node.js中g(shù)runt和gulp的區(qū)別詳解

    node.js中g(shù)runt和gulp的區(qū)別詳解

    這篇文章主要介紹了node.js中g(shù)runt和gulp的區(qū)別詳解的相關(guān)資料,需要的朋友可以參考下
    2017-07-07
  • npm配置國(guó)內(nèi)鏡像資源+淘寶鏡像的方法

    npm配置國(guó)內(nèi)鏡像資源+淘寶鏡像的方法

    這篇文章主要介紹了npm配置國(guó)內(nèi)鏡像資源+淘寶鏡像的方法,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-09-09

最新評(píng)論

海南省| 青岛市| 台南县| 新密市| 大化| 小金县| 修水县| 黄龙县| 关岭| 江华| 张家界市| 张家港市| 浠水县| 大同县| 固始县| 彭水| 平湖市| 延吉市| 慈溪市| 胶南市| 洞头县| 高邮市| 四子王旗| 广灵县| 台湾省| 芮城县| 澄江县| 沁阳市| 黄冈市| 大竹县| 巢湖市| 汽车| 日喀则市| 白朗县| 芜湖市| 崇信县| 金溪县| 同德县| 个旧市| 苍南县| 佛山市|