iT邦幫忙

4

【我可以你也可以的Node.js】第十一篇 - Process 的標準串流 (stdin、stdout、stderr)

這篇是延續以下文章的內容
第四篇 - 進程、行程、過程、行程、程序、處理程序、Process
第五篇 - Task queue、EventLoop
第六篇 - Node's EventLoop和Node程式執行順序
第七篇 - 第一次用EventEmiter就用來做壞壞的事ヽ(́◕◞౪◟◕‵)ノ
如果還沒看過以上內容的可自行斟酌觀看。

上次只提到可以透過 process 物件查看 Node 環境等資訊,
這篇主要會講 process 的 I/O(標準輸出入)。
但我覺得其實不影響此篇獨立觀看~
只是順邊幫以前的文章打一點廣告

此篇學習目標 ◑ω◐ :

  • Process標準串流是什麼
  • 利用 stdin 讀取串流
  • 利用 stdout 與 stderr 分別處理串流輸出
  • 實際使用!建立簡單的 CLI 來查詢鼠年馬拉松的文章登記情況

Process標準串流是什麼

process 的 I/O 函式其實是繼承自 EventEmitter,這代表說我們可以發出事件或是監聽事件擷取任何資料來做處理,而且這些串流是無敵的!你沒辦法把它關閉或是在應用程式中刪除掉。
聽起來是不是特別的令人興奮啊!

而串流在 Node 中主要分別由 stdin(標準輸入)、 stdout (標準輸出) 、 stderr (標準錯誤) 所組成。
而 process 會以三種函式來使用

  • process.stdin 讀取串流
  • process.stdout 寫入串流
  • process.stderr 寫入串流

這邊你應該會想說...
咦(((゚д゚)))!
怎麼 stdoutstderr 都是寫入串流,感覺是不是一樣的啊?
其實他是刻意這樣分的,多建構一個 stderr 是為了想要分別處理預期的輸出與發生問題的輸出,我相信你不會想要特地在同一個地方還要自行判斷和分類的對吧!

我相信正在讀的你已經迫不急待想做一些嘿咻臉的事了 ⁽⁽٩(๑˃̶͈̀ ᗨ ˂̶͈́)۶⁾⁾

利用 stdin 讀取串流

首先必須設定串流的編碼...

process.stdin.setEncoding('utf-8');

不然會發生很可怕的事...
就是... 看不懂 xDDD
如果沒有設定只是結果會輸出 buffer 而不是字串而已啦 (´⊙ω⊙`)
至於 buffer 是什麼有興趣可以自行研究,之後"應該"有機會我也會分享,因為我現在不想誤導各位。

接下來我們監聽 readable 事件,這個事件主要是會讓我們知道有資料可以讀取的事件,然後再藉由 process.stdin.read() 讀取串流

process.stdin.setEncoding('utf-8');

process.stdin.on('readable', () => {
    const input = process.stdin.read();
    if (input !== null) {
        console.log(`我監聽到東西啦 -> ${input}`)
    }
});

這邊就判斷如果接收到的資訊不是 null 就會輸出監聽到的東西。

實際執行情況:

利用 stdout 與 stderr 分別處理串流輸出

這部分我會搭配上面的 stdin 使用,
上面有提到這兩個都是串流輸出但是主要是將預期與非預期的輸出結果作分流。
在 browser 的開發人員工具可以明顯分出 error 和 info 的 console,
你只要想成以下這樣就好

console.log() -> process.stdout()
console.error() -> process.stderr()

但是在 terminal 這塊我看不出來差異 Orz

那這邊就用如果輸入 Robin is handsome 就代表是預期的,
並且輸出串流 ✅Very smart! That is truth.

反之如果輸入 Kevin have girlfriend 就代表這不是預期的,
並且輸出串流 ❌Liyer! That is not truth.
而其餘情況就不多做事。

process.stdin.setEncoding('utf-8');

process.stdin.on('readable', () => {
    let input;
    while ((input = process.stdin.read()) !== null) {
        const command = input.trim(); \\去除兩端的空白字符或是斷行
        console.log(`我監聽到東西啦 -> ${command}`)
        if (command === 'Robin is handsome') process.stdout.write("✅Very smart! That is truth.")
        if (command === 'Kevin have girlfriend') process.stderr.write("❌Liyer! That is not truth.")
        if (command === 'exit') process.exit();
    }
});

如果不想要連同換行 也被log 出來,可以改用以下判斷
if(command!== '') console.log(`我監聽到東西啦 -> ${command}`)
另外如果不想使用trim() 可以在判斷的最後方加入'\n'也可以正常運行唷~

實際執行情況:

實際使用!建立簡單的 CLI 來查詢鼠年馬拉松的文章登記情況

這邊就直接實戰,以直接想查詢自己在六角學院這次辦的鼠年馬拉松,登記情況為例,剛好六角學院這邊有提供 API 真的方便。
雖然說有人有做網頁版滿方便且漂亮的,但是實際上還是要開瀏覽器,
如果想單純使用 terminal 查詢就可以參考我這個範例喔~
先看成果~

我的使用情境是我希望我能夠打 get 關鍵字然後再輸入我的keyID來查詢我的文章登記情況,像我的keyID就是 188
那我就希望我只需要在 termnial 輸入 get 188,就能拿到我要的東西。
來吧~
首先寫一個獲取鼠年馬拉松API資料的的module。

//getW3hexschool.js

const request = require('request');
const w3hexschool_API_URL = 'https://raw.githubusercontent.com/hexschool/w3hexschool-API/master/data.json'
module.exports = function (keyID) {
    return new Promise((resolve, reject) => {
        request.get({ 'url': w3hexschool_API_URL }, (error, response) => {
            if (error) reject(new Error(error));
            resolve(response);
        });
    });
}

再來就是今天學到的使用 process 標準輸出入

// cli.js

const getW3hexschool = require('./getW3hexschool');
process.stdin.setEncoding('utf-8');

process.stdin.on('readable', () => {
  let input;
  while ((input = process.stdin.read()) !== null) {
    const command = input.trim();
    if (command.includes('get') && command.split(' ').length === 2) {
      const keyID = parseInt(command.split(' ')[1]);
      getW3hexschool(keyID).then(val => {
        const targetVal = (JSON.parse(val.body)).filter((item) => {
          return item.keyID === keyID;
        });
        console.log('-------------------------------------')
        process.stdout.write(`name: ${targetVal[0].name}\n`);
        process.stdout.write(`blogUrl: ${targetVal[0].blogUrl}\n`);
        process.stdout.write(`updateTime: ${targetVal[0].updateTime}\n`);
        process.stdout.write('blogList:\n');
        targetVal[0].blogList.forEach(item => {
          process.stdout.write(`Title: ${item.title}\n`);
          process.stdout.write(`Url: ${item.url}\n`);
        });
        console.log('-------------------------------------');
      });
    };
    if (command === 'exit') process.exit();
    }
});

我這邊的判斷方法是
先判斷 user 輸入的字有沒有包含 get,且中間以空白為區隔,區隔出來長度必須是2 ,如果成立就拿區隔出來的第二個值當作是 keyID,再把 拿到的資料透過filter將keyID做篩選,最後列出來。

當然你也可以將這程式改成你要的,可能你想要輸入名字之類的,就依照您自己的需求做改良。(,,・ω・,,)

以上。
有問題都可以留言問我
感謝各位讀者的觀看


參考文獻

Node doc
process.stdin
Node.js 基于流将日志、错误分别写入不同文件


1 則留言

0
Joe
iT邦新手 4 級 ‧ 2020-05-04 11:50:22

大大你好,我想問 process 的 I/O 函式 和 socket 有甚麼差異嗎?
因為感覺兩者很像

Robin iT邦新手 3 級 ‧ 2020-05-06 13:34:29 檢舉

感謝您的提問
我覺得像的地方應該在於說他都是繼承 EventEmiter
process的 I/O 函式比較偏向local端應用的範圍。
而據我所知 socket 的應用比較偏向client端與伺服器的溝通
我想像的感覺只是用法和概念上很類似xD 但是牽扯的範圍感覺有滿大的差異。

我自己目前學習下來感覺滿多函式都是繼承 EventEmiter
這部分我也有寫一個文章可以參考看看
https://ithelp.ithome.com.tw/articles/10230520

以上是我的看法啦,對於 node 的 socket 詳細我也還沒深入研究,只是依照我以前有碰過 python 的 socket 和剛剛稍微看一下用法所回答的/images/emoticon/emoticon02.gif

我要留言

立即登入留言