欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

node實(shí)現(xiàn)shell命令管理工具及commander.js學(xué)習(xí)

 更新時(shí)間:2022年09月09日 15:05:32   作者:lulu_up  
這篇文章主要為大家介紹了node實(shí)現(xiàn)shell命令管理工具及commander.js學(xué)習(xí),有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

背景、

github 地址: https://github.com/lulu-up/record-shell

你有沒有經(jīng)歷過忘記某個(gè)shell命令怎么拼寫? 或是懶得打一長(zhǎng)串命令的經(jīng)歷? 比如我的mac筆記本的tachbar偶爾會(huì)'卡死', 這時(shí)我就要輸入 killall ControlStrip 命令重啟tachbar, 你也看到了這個(gè)命令真心懶得打。

還有新建react項(xiàng)目我每次都要輸入npx create-react-app 項(xiàng)目名 --template typescript, 在公司的日常開發(fā)中我習(xí)慣每次寫新需求都單獨(dú)clone項(xiàng)目并創(chuàng)建新的分支進(jìn)行開發(fā), 此時(shí)就需要去gitlab上復(fù)制項(xiàng)目地址然后在本地git clone xxxxxxxxxx 新的項(xiàng)目名, 理論上這些操作真的很重復(fù)。

首先本次要帶你用node一起動(dòng)手做一款記錄shell命令的小插件, 當(dāng)然網(wǎng)上類似插件也是有的, 但我這次做了一個(gè)最簡(jiǎn)單粗暴的版本, 自己用著也爽的版本, 并且也想趁機(jī)溫習(xí)一遍命令行相關(guān)知識(shí)。

一、用法演示

先一起看看這個(gè)'庫'是否真的方便:

1: 安裝

npm install record-shell -g

安裝完畢你的全局會(huì)多出 rs命令:

2: 添加

rs add

起名隨意, 甚至全用漢語更舒服, 這里先演示輸入簡(jiǎn)單命令:

3: 查看 + 使用'

rs ls

命令是可選擇的, 這里我先多加幾個(gè)湊所的命令用來演示:

可以按上下鍵移動(dòng)選擇, 回車即可執(zhí)行命令:

當(dāng)然也可以查看命令詳情, 只需-a參數(shù):

rs ls -a

4: 移除

rs rm

5: add有變量的命令

我們的命令當(dāng)然不會(huì)都是寫'死'的模式啦, 比如命令 echo 內(nèi)容 > a.txt, 這里的意思是我要把內(nèi)容寫入目標(biāo)文件:

6: 使用變量

使用命令時(shí)會(huì)引導(dǎo)我們填入變量, 所以定義時(shí)寫漢語就行:

二、初始化自己的node項(xiàng)目

接下來一起從零開始做出這個(gè)庫, 考慮到一些新手同學(xué)可能沒做過這種全局的node包, 我這里就講的詳細(xì)一些。

初始化項(xiàng)目沒啥好說的, 隨便起名:

npm init

改造package.json文件:

"bin": {
    "rs": "./bin/www"
  },

這里在 bin內(nèi)指明, 當(dāng)運(yùn)行 rs 命令的時(shí)候, 訪問"./bin/www"。

#! /usr/bin/env node
require('../src/index.js')
  • #! 這個(gè)符號(hào)通常在Unix系統(tǒng)的基本中第一行開頭中出現(xiàn),用于指明這個(gè)腳本文件的解釋程序。
  • /usr/bin/env 因?yàn)榭赡艽蠹視?huì)把node安裝到不同的目錄下, 這里直接告訴系統(tǒng)可以在PATH目錄中查找, 這樣就兼容了不同的node安裝路徑。
  • node 這個(gè)自不必說, 就是去查找咱們的node命令。

三、初始化命令 + 全局安裝

這里講一下如何將我們的命令掛在到全局, 使你可以在任何地方都能使用全局的rs命令:

// cd 我們的項(xiàng)目
npm install . -g

這里比較好理解吧, 相當(dāng)于直接把項(xiàng)目安裝在了全局, 我們平時(shí)install xxx -g 是去遠(yuǎn)端拉取, 這個(gè)命令是拉當(dāng)前目錄。

此時(shí)那你向index.js文件內(nèi)寫入console.log('全局執(zhí)行'), 再全局執(zhí)行 rs 并看到如下效果就是成功了:

四、commander.js (node命令行解決方案)

先安裝再聊:

npm install commander

commander的可以幫我們非常規(guī)范的處理用戶的命令, 比如用戶在命令行輸入rs ls -a, 原生node的情況下我可以先將輸入的args進(jìn)行拆解, 拆解出 ls 與 -a, 然后再寫一堆if判斷如果是ls并且后面有-a則如何去做, 但顯然這樣寫不規(guī)范, 代碼也難以維護(hù), commander就是來幫我們規(guī)范這些寫法的:

將下面的代碼放進(jìn) index.js文件中:

const fs = require("fs");
const path = require("path");
const program = require('commander');
const packagePath = path.join(__dirname, "../package.json")
const packageData = JSON.parse(fs.readFileSync(packagePath, 'utf-8'));
program.version(packageData.version)
program
    .command('ls [-type]')
    .description('description')
    .action((value) => {
        console.log('你輸入的是:', value)
    })
program.parse(process.argv)

在命令行輸入:

rs ls 123456

逐句解釋一下代碼:

  • const program = require('commander')這里很明顯引入了commander。
  • program.version(packageData.version)此處是定義了當(dāng)前的版本, 當(dāng)你輸入rs -V時(shí)會(huì)展示program.version方法獲取到的值, 此處直接使用了package.json里面的version字段。
  • program.command('ls') 定義了名為ls的參數(shù), 當(dāng)我們輸入rs ls時(shí)才會(huì)觸發(fā)我們后面的處理方法, 我之所以寫成program.command('ls [-type]')是因?yàn)榧由?code>[-type]后commander才會(huì)認(rèn)為ls命令后面可以跟其他參數(shù), 當(dāng)然你叫[xxxxx]也可以, 讓使用者能看懂即可。

.description('description')顧名思義這里是簡(jiǎn)介描述, 當(dāng)我們輸入rs -h的時(shí)候會(huì)出現(xiàn):

  • .action方法就是commander檢測(cè)到當(dāng)前命令觸發(fā)時(shí)的處理函數(shù), 第一個(gè)參數(shù)是用戶傳入的參數(shù), 第二個(gè)參數(shù)是Command對(duì)象, 后續(xù)我們會(huì)在這里彈出選擇列表。
  • process.argv這里要先知道processnode中的全局變量, 其中argv是啟動(dòng)命令行時(shí)的所有參數(shù)。
  • program.parse(process.argv)看完上面這里就好理解了, 將命令行參數(shù)傳遞給commander開始執(zhí)行。

番外

如果你配置program.option('ls', 'ls的介紹'), 則當(dāng)用戶輸入rs -h時(shí)會(huì)出現(xiàn), 但我感覺加了有點(diǎn)亂, 咱們的插件追求簡(jiǎn)單所以就沒加。

五、inquirer.js(node命令行交互插件)

npm install inquirer

 inquirer可以幫我們生成各種命令行問答功能, 就像vue-cli差不多的效果, 大家可以輸入下面代碼試一試'單選模式':

program
    .command('ls [-type]')
    .description('description')
    .action(async (value) => {
        const answer = await inquirer.prompt([{
            name: "key",
            type: "rawlist",
            message: "message1",
            choices: [
                {
                    name: 'name1',
                    value: 'value1'
                },
                {
                    name: 'name2',
                    value: 'value2'
                }
            ]
        }])
        console.log(answer)
    })

逐句解釋一下代碼:

  • 首先這里是一個(gè)asyncawite的模式。
  • inquirer.prompt參數(shù)是一個(gè)數(shù)組, 因?yàn)樗梢赃B續(xù)操作, 比如進(jìn)行兩次單選列表操作。
  • name就是最終的key, 比如namexxxx用戶選擇了1, 則最終返回結(jié)果就是{xxxx:1}。
  • type指定交互類型rawlist單選列表、 input輸入、checkbox多選列表等。
  • message就是提示語, 我們讓用戶選擇之前總要告訴他這里在做啥吧。
  • choices選項(xiàng)的數(shù)組, name選項(xiàng)名, value選項(xiàng)值。

六、添加命令: add

正式開始做第一個(gè)命令, 我新建了一個(gè)名為env的文件夾, 里面創(chuàng)建record-list.json文件用了存儲(chǔ)用戶的命令:

 add命令無非就是往record-list.json文件里面增加內(nèi)容:

program
    .command('add')
    .description('添加命令')
    .action(async () => {
        const answer = await inquirer.prompt([{
            name: "name",
            type: "input",
            message: "命令名稱:",
            validate: ((name) => {
                if (name !== '') return true
            })
        }, {
            name: "command",
            type: "input",
            message: "命令語句, 可采用[var]的形式傳入變量:",
            validate: ((command) => {
                if (command !== '') return true
            })
        }])
          let shellList = getShellList();
          shellList = shellList.filter((item) => item.name !== answer.name);
          shellList.push({
             "name": answer.name,
             "command": answer.command
          })
          fs.writeFileSync(dataPath, JSON.stringify(shellList));
    })

逐句解釋一下代碼:

  • 首先我們使用commander定義了add命令;
  • 當(dāng)觸發(fā)add命令時(shí)我們使用inquirer定義了兩個(gè)輸入框, 第一個(gè)輸入命令名稱, 第二個(gè)輸入命令語句。
  • validate定義了對(duì)入?yún)⒌男r?yàn), 注意: 用戶不輸入值不是undefined而是空字符串, 所以使用了 !== '', 如果校驗(yàn)不通過無法繼續(xù)操作。
  • 用戶填寫完畢就向record-list.json添加數(shù)據(jù), 同時(shí)如果是重名的命令就進(jìn)行替換。

名稱可能會(huì)重復(fù), 但是不要緊, 因?yàn)樗氖褂脠?chǎng)景決定了它不需要做過多的限制。

七、移除命令: rm

這里的原理就是拉取record-list.json數(shù)據(jù)進(jìn)行刪減, 然后更新record-list.json:

program
    .command('rm')
    .description('移除命令')
    .action(async () => {
        let shellList = getShellList();
        const choices = shellList.map((item) => ({
            key: item.name,
            name: item.name,
            value: item.name,
        }));
        const answer = await inquirer.prompt([{
            name: "names",
            type: "checkbox",
            message: `請(qǐng)'選擇'要?jiǎng)h除的記錄`,
            choices,
            validate: ((_choices) => {
                if (_choices.length) return true
            })
        }])
        shellList = shellList.filter((item) => {
            return !answer.names.includes(item.name)
        })
        fs.writeFileSync(dataPath, JSON.stringify(shellList));
    })

逐句解釋一下代碼:

  • choices是定義了一組可選項(xiàng)。
  • 使用checkbox多選模式, 讓用戶可以一次刪除多個(gè)命令。
  • validate校驗(yàn)了什么都不刪的情況, 因?yàn)榭赡苁褂脩敉它c(diǎn)擊選取(空格鍵)。
  • 使用filter過濾掉名稱相同的命令。
  • 最后更新record-list.json文件。

八、查看+使用: ls

這里內(nèi)容稍微多一點(diǎn), 畢竟一個(gè)命令負(fù)責(zé)兩個(gè)能力, 這里的核心原理是拉取record-list.json文件的內(nèi)容展示成單選列表, 然后根據(jù)用戶選取的值進(jìn)行命令的執(zhí)行, 最后返回執(zhí)行結(jié)果;

1: 查看ls, 支持傳參 -a

program
    .command('ls')
    .alias('l')
    .description('命令列表')
    .option('-a detailed')
    .action(async (_, options) => {
        const shellList = getShellList();
        const choices = shellList.map(item => ({
            key: item.name,
            name: `${item.name}${options.detailed ? ': ' + item.command : ''}`,
            value: item.command
        }));
        if (choices.length === 0) {
            console.log(`
            您當(dāng)前沒有錄入命令, 可使用'rs add' 進(jìn)行添加
            `)
            return
        }
        const answer = await inquirer.prompt([{
            name: "key",
            type: "rawlist",
            message: "選擇要執(zhí)行的命令",
            choices
        }])
    })

逐句解釋一下代碼:

  • option('-a detailed')定義了可以接收-a參數(shù), 比如ls -a, 并且如果用戶傳了-a則會(huì)得到返回值{detailed: true}。
  • 如果有-a則將命令本身放在name屬性里展示出來。
  • choices是轉(zhuǎn)換了record-list.json文件里的數(shù)據(jù)的列表數(shù)據(jù)。
  • 如果record-list.json數(shù)據(jù)是空的, 則提示用戶去使用rs add進(jìn)行添加。
  • 使用inquirer生成單選列表。

2: 判斷命令語句中是否有變量

由于允許用戶輸入的命令內(nèi)帶變量, 比如前面演示過的 echo [內(nèi)容] > [文件名], 那我就要判斷當(dāng)前用戶選中的命令內(nèi)是否有變量:

const optionsReg = /\[.*?\]/g;
function getShellOptions(command) {
    const arr = command.match(optionsReg) || [];
    if (arr.length) {
        return arr.map((message) => ({
            name: message,
            type: "input",
            message,
        }));
    } else {
        return []
    }
}

逐句解釋一下代碼:

  • optionsReg正則匹配出所有 '[這種寫法]'的變量。
  • 如果匹配到了變量則返回一個(gè)數(shù)組, 這個(gè)數(shù)組的長(zhǎng)度是變量的個(gè)數(shù), 因?yàn)槊總€(gè)變量都要有一次輸入的機(jī)會(huì)。
  • 沒有對(duì)重復(fù)的name進(jìn)行特殊處理, 并且name會(huì)變成返回值的key, 所以不可以重名, 重名的話回會(huì)導(dǎo)致只處理第一個(gè)變量。

3: 無變量 -> 執(zhí)行

這里有一個(gè)新的概念:

const child_process = require('child_process');

 child_process可以生成node的'子進(jìn)程', child_process.exec方法是啟動(dòng)了一個(gè)系統(tǒng)shell來解析參數(shù),因此可以是非常復(fù)雜的命令,包括管道和重定向。

child_process.exec(command, function (error, stdout) {
        console.log(`${stdout}`)
        if (error !== null) {
            console.log('error: ' + error);
        }
    });

逐句解釋一下代碼:

  • command是要執(zhí)行的命令。
  • stdout執(zhí)行命令的輸出, 比如ls就是輸出當(dāng)前目錄中的文件信息。
  • error這里也很重要, 如果報(bào)錯(cuò)了要讓用戶知道報(bào)錯(cuò)信息, 所以也console了。

4: 有變量 -> 執(zhí)行

核心原理是解析'變量'后對(duì)命令語句進(jìn)行替換, 然后正常執(zhí)行就ok:

function answerOptions2Command(command, answerMap) {
    for (let key in answerMap) {
        command = command.replace(`[${key}]`, answerMap[key])
    }
    return command;
}
function handleExec(command) {
    child_process.exec(command, function (error, stdout) {
        console.log(`${stdout}`)
        if (error !== null) {
            console.log('error: ' + error);
        }
    });
}
 if (shellOptions.length) {
        const answerMap = await inquirer.prompt(shellOptions)
        const command = answerOptions2Command(answer.key, answerMap)
        handleExec(command)
    } else {
        handleExec(answer.key)
    }

逐句解釋一下代碼:

  • inquirer執(zhí)行完會(huì)返回一個(gè)字典, 比如{[文本]:"xxxxx", [文件名]:"a.txt"}, 因?yàn)槲覀冊(cè)O(shè)置了namemessage使用同樣的名稱。
  • answerOptions2Command循環(huán)執(zhí)行replace進(jìn)行變量的替換。
  • handleExec負(fù)責(zé)執(zhí)行語句。

九、讓文字變色 (chalk)

功能都完成了, 但是我們的提示文字還是'黑白的', 我們當(dāng)然希望命令行中多姿多彩一些, 在node中使用:

var red = "\033[31m red \033[0m";
console.log('你好紅色:', red)

 \033c語言中的轉(zhuǎn)義字符這里就不擴(kuò)了, 反正看到他就是要對(duì)屏幕進(jìn)行操作了, 但是我們可以看出上面的寫法很不友好, 肯定要封裝一下下, chalk.js就是個(gè)不錯(cuò)的已有輪子, 我們下進(jìn)行安裝:

npm install chalk

使用:

const chalk = require('chalk') 
chalk.red('你好: 紅色')

你高興太早了, 現(xiàn)在是有問題的 !!

其他教程里都沒說怎么解決, 其實(shí)那你只要把chalk的版本降低到4就ok了!

以上就是node實(shí)現(xiàn)shell命令管理工具及commander.js學(xué)習(xí)的詳細(xì)內(nèi)容,更多關(guān)于node shell命令管理的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • node版本管理器Volta的簡(jiǎn)單使用

    node版本管理器Volta的簡(jiǎn)單使用

    Volta是一款強(qiáng)大的JavaScript工具管理器,它簡(jiǎn)化了命令行工具的安裝和管理,本文主要介紹了node版本管理器Volta的簡(jiǎn)單使用,具有一定的參考價(jià)值,感興趣的可以了解一下
    2024-02-02
  • 淺析node.js的模塊加載機(jī)制

    淺析node.js的模塊加載機(jī)制

    這篇文章主要介紹了淺析node.js的模塊加載機(jī)制,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2018-05-05
  • nodejs微信開發(fā)之接入指南

    nodejs微信開發(fā)之接入指南

    這篇文章主要介紹了nodejs微信開發(fā)之接入指南,小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧
    2019-03-03
  • 從一個(gè)爬蟲開始ChatGPT的編程秀

    從一個(gè)爬蟲開始ChatGPT的編程秀

    這篇文章主要為大家介紹了從一個(gè)爬蟲開始ChatGPT的編程秀,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-03-03
  • node.js到底要不要加分號(hào)淺析

    node.js到底要不要加分號(hào)淺析

    這篇文章主要給大家介紹了關(guān)于node.js到底要不要加分號(hào)的相關(guān)資料,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2018-07-07
  • 用node.js寫一個(gè)jenkins發(fā)版腳本

    用node.js寫一個(gè)jenkins發(fā)版腳本

    這篇文章主要介紹了用node.js寫一個(gè)jenkins發(fā)版腳本,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-05-05
  • node-red File讀取好保存實(shí)例講解

    node-red File讀取好保存實(shí)例講解

    在本篇文章里小編給大家整理了關(guān)于node-red File讀取好保存的相關(guān)知識(shí)點(diǎn)內(nèi)容,有需要的朋友們可以參考下。
    2019-09-09
  • 詳解Node.js一行命令上傳本地文件到服務(wù)器

    詳解Node.js一行命令上傳本地文件到服務(wù)器

    這篇文章主要介紹了Node.js一行命令上傳本地文件到服務(wù)器,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04
  • node.js下when.js 的異步編程實(shí)踐

    node.js下when.js 的異步編程實(shí)踐

    這篇文章主要介紹了node.js下when.js 的異步編程實(shí)踐,需要的朋友可以參考下
    2014-12-12
  • Node如何后臺(tái)數(shù)據(jù)庫使用增刪改查功能

    Node如何后臺(tái)數(shù)據(jù)庫使用增刪改查功能

    這篇文章主要介紹了Node如何后臺(tái)數(shù)據(jù)庫使用增刪改查功能,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下
    2019-11-11

最新評(píng)論