Nodejs 進階:readline 實現日誌分析 + 簡易命令列工具

程式猿小卡_casper發表於2019-03-01

本文摘錄自《Nodejs學習筆記》,更多章節及更新,請訪問 github主頁地址。歡迎加群交流,群號 197339705

模組概覽

readline是個非常實用的模組。如名字所示,主要用來實現逐行讀取,比如讀取使用者輸入,或者讀取檔案內容。常見使用場景有下面幾種,本文會逐一舉例說明。本文相關程式碼可在筆者github上找到。

  • 檔案逐行讀取:比如說進行日誌分析。
  • 自動完成:比如輸入npm,自動提示”help init install”。
  • 命令列工具:比如npm init這種問答式的腳手架工具。

基礎例子

先看個簡單的例子,要求使用者輸入一個單詞,然後自動轉成大寫

const readline = require(`readline`);

const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout
});

rl.question(`Please input a word: `, function(answer){
    console.log(`You have entered {%s}`, answer.toUpperCase());
    rl.close();
});複製程式碼

執行如下:

➜  toUpperCase git:(master) ✗ node app.js 
Please input a word: hello
You have entered {HELLO}複製程式碼

例子:檔案逐行讀取:日誌分析

比如我們有如下日誌檔案access.log,我們想要提取“訪問時間+訪問地址”,藉助readline可以很方便的完成日誌分析的工作。

[2016-12-09 13:56:48.407] [INFO] access - ::ffff:127.0.0.1 - - "GET /oc/v/account/user.html HTTP/1.1" 200 213125 "http://www.example.com/oc/v/account/login.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36"
[2016-12-09 14:00:10.618] [INFO] access - ::ffff:127.0.0.1 - - "GET /oc/v/contract/underlying.html HTTP/1.1" 200 216376 "http://www.example.com/oc/v/account/user.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36"
[2016-12-09 14:00:34.200] [INFO] access - ::ffff:127.0.0.1 - - "GET /oc/v/contract/underlying.html HTTP/1.1" 200 216376 "http://www.example.com/oc/v/account/user.html" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_11_4) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/54.0.2840.98 Safari/537.36"複製程式碼

程式碼如下:

const readline = require(`readline`);
const fs = require(`fs`);

const rl = readline.createInterface({
    input: fs.createReadStream(`./access.log`)
});

rl.on(`line`, (line) => {
    const arr = line.split(` `); 
    console.log(`訪問時間:%s %s,訪問地址:%s`, arr[0], arr[1], arr[13]);
});複製程式碼

執行結果如下:

➜  lineByLineFromFile git:(master) ✗ node app.js
訪問時間:[2016-12-09 13:56:48.407],訪問地址:"http://www.example.com/oc/v/account/login.html"
訪問時間:[2016-12-09 14:00:10.618],訪問地址:"http://www.example.com/oc/v/account/user.html"
訪問時間:[2016-12-09 14:00:34.200],訪問地址:"http://www.example.com/oc/v/account/user.html"複製程式碼

例子:自動完成:程式碼提示

這裡我們實現一個簡單的自動完成功能,當使用者輸入npm時,按tab鍵,自動提示使用者可選的子命令,如help、init、install。

  • 輸入np,按下tab:自動補全為npm
  • 輸入npm in,按下tab:自動提示可選子命令 init、install
  • 輸入npm inst,按下tab:自動補全為 npm install
const readline = require(`readline`);
const fs = require(`fs`);

function completer(line) {
    const command = `npm`;
    const subCommands = [`help`, `init`, `install`];

    // 輸入為空,或者為npm的一部分,則tab補全為npm
    if(line.length < command.length){
        return [command.indexOf(line) === 0 ? [command] : [], line];
    }

    // 輸入 npm,tab提示 help init install
    // 輸入 npm in,tab提示 init install
    let hits = subCommands.filter(function(subCommand){ 
        const lineTrippedCommand = line.replace(command, ``).trim();
        return lineTrippedCommand && subCommand.indexOf( lineTrippedCommand ) === 0;
    })

    if(hits.length === 1){
        hits = hits.map(function(hit){
            return [command, hit].join(` `);
        });
    }

    return [hits.length ? hits : subCommands, line];
}

const rl = readline.createInterface({
  input: process.stdin,
  output: process.stdout,
  completer: completer
});

rl.prompt();複製程式碼

程式碼執行效果如下,當輸入npm in,按下tab鍵,則會自動提示可選子命令init、install。

➜  autoComplete git:(master) ✗ node app.js
> npm in
init     install複製程式碼

例子:命令列工具:npmt init

下面藉助readline實現一個迷你版的npm init功能,執行指令碼時,會依次要求使用者輸入name、version、author屬性(其他略過)。

這裡用到的是rl.question(msg, cbk)這個方法,它會在控制檯輸入一行提示,當使用者完成輸入,敲擊回車,cbk就會被呼叫,並把使用者輸入作為引數傳入。

const readline = require(`readline`);
const fs = require(`fs`);
const rl = readline.createInterface({
    input: process.stdin,
    output: process.stdout,
    prompt: `OHAI> `
});

const preHint = `
This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.
`;

console.log(preHint);

// 問題
let questions = [ `name`, `version`, `author`];

// 預設答案
let defaultAnswers = [ `name`, `1.0.0`, `none` ];

// 使用者答案
let answers = [];
let index = 0;

function createPackageJson(){
    var map = {};
    questions.forEach(function(question, index){
        map[question] = answers[index];
    });

    fs.writeFileSync(`./package.json`, JSON.stringify(map, null, 4));
}

function runQuestionLoop() {

    if(index === questions.length) {
        createPackageJson();
        rl.close();
        return;
    }

    let defaultAnswer = defaultAnswers[index];
    let question = questions[index] + `: (` + defaultAnswer +`) `;

    rl.question(question, function(answer){
        answers.push(answer || defaultAnswer);
        index++;
        runQuestionLoop();
    });
}

runQuestionLoop();複製程式碼

執行效果如下,最後還像模像樣的生成了package.json(害羞臉)。

commandLine git:(master) ✗ node app.js

This utility will walk you through creating a package.json file.
It only covers the most common items, and tries to guess sensible defaults.

See `npm help json` for definitive documentation on these fields
and exactly what they do.

Use `npm install <pkg> --save` afterwards to install a package and
save it as a dependency in the package.json file.

Press ^C at any time to quit.

name: (name) hello
version: (1.0.0) 0.0.1
author: (none) chyingp複製程式碼

寫在後面

有不少基於readline的有趣的工具,比如各種腳手架工具。限於篇幅不展開,感興趣的同學可以研究下。

相關連結

nodejs.org/api/readlin…

相關文章