Readline#

穩定性:2 - 穩定

原始碼: lib/readline.js

node:readline 模組提供了一個介面,用於從可讀流(例如 process.stdin)中一次一行地讀取資料。

要使用基於 Promise 的 API

import * as readline from 'node:readline/promises';const readline = require('node:readline/promises');

要使用回撥和同步 API

import * as readline from 'node:readline';const readline = require('node:readline');

以下簡單示例說明了 node:readline 模組的基本用法。

import * as readline from 'node:readline/promises';
import { stdin as input, stdout as output } from 'node:process';

const rl = readline.createInterface({ input, output });

const answer = await rl.question('What do you think of Node.js? ');

console.log(`Thank you for your valuable feedback: ${answer}`);

rl.close();const readline = require('node:readline');
const { stdin: input, stdout: output } = require('node:process');

const rl = readline.createInterface({ input, output });

rl.question('What do you think of Node.js? ', (answer) => {
  // TODO: Log the answer in a database
  console.log(`Thank you for your valuable feedback: ${answer}`);

  rl.close();
});

一旦呼叫此程式碼,Node.js 應用程式將不會終止,直到 readline.Interface 被關閉,因為該介面會等待在 input 流上接收資料。

類:InterfaceConstructor#

InterfaceConstructor 類的例項是使用 readlinePromises.createInterface()readline.createInterface() 方法構造的。每個例項都與一個 input 可讀流和一個 output 可寫流相關聯。output 流用於為到達 input 流並從中讀取的使用者輸入列印提示。

事件:'close'#

當發生以下任一情況時,將觸發 'close' 事件:

  • 呼叫 rl.close() 方法,且 InterfaceConstructor 例項已放棄對 inputoutput 流的控制;
  • input 流接收到其 'end' 事件;
  • input 流接收到 Ctrl+D 來表示傳輸結束 (EOT);
  • input 流接收到 Ctrl+C 來表示 SIGINT,並且 InterfaceConstructor 例項上沒有註冊 'SIGINT' 事件監聽器。

呼叫監聽器函式時不傳遞任何引數。

一旦發出 'close' 事件,InterfaceConstructor 例項就完成了。

事件:'line'#

每當 input 流接收到行尾輸入(\n\r\r\n)時,就會觸發 'line' 事件。這通常在使用者按下 Enter 鍵或 Return 鍵時發生。

如果從流中讀取了新資料,並且該流在沒有最終行尾標記的情況下結束,也會觸發 'line' 事件。

監聽器函式被呼叫時,會傳入一個包含接收到的單行輸入的字串。

rl.on('line', (input) => {
  console.log(`Received: ${input}`);
}); 

事件:'history'#

每當歷史記錄陣列發生變化時,就會觸發 'history' 事件。

監聽器函式被呼叫時,會傳入一個包含歷史記錄陣列的陣列。它將反映所有更改,包括由於 historySizeremoveHistoryDuplicates 而新增和刪除的行。

其主要目的是允許監聽器持久化歷史記錄。監聽器也可以更改歷史記錄物件。這對於防止某些行(如密碼)被新增到歷史記錄中很有用。

rl.on('history', (history) => {
  console.log(`Received: ${history}`);
}); 

事件:'pause'#

當發生以下任一情況時,將觸發 'pause' 事件:

  • input 流被暫停。
  • input 流未暫停,並接收到 'SIGCONT' 事件。(參見事件 'SIGTSTP''SIGCONT'。)

呼叫監聽器函式時不傳遞任何引數。

rl.on('pause', () => {
  console.log('Readline paused.');
}); 

事件:'resume'#

每當 input 流恢復時,就會觸發 'resume' 事件。

呼叫監聽器函式時不傳遞任何引數。

rl.on('resume', () => {
  console.log('Readline resumed.');
}); 

事件:'SIGCONT'#

當一個 Node.js 程序先前使用 Ctrl+Z(即 SIGTSTP)移至後臺,然後使用 fg(1p) 重新帶回前臺時,會觸發 'SIGCONT' 事件。

如果在 SIGTSTP 請求之前 input 流被暫停,則不會觸發此事件。

呼叫監聽器函式時不傳遞任何引數。

rl.on('SIGCONT', () => {
  // `prompt` will automatically resume the stream
  rl.prompt();
}); 

'SIGCONT' 事件在 Windows 上支援。

事件:'SIGINT'#

input 流接收到 Ctrl+C 輸入(通常稱為 SIGINT)時,會觸發 'SIGINT' 事件。如果在 input 流接收到 SIGINT 時沒有註冊 'SIGINT' 事件監聽器,則會觸發 'pause' 事件。

呼叫監聽器函式時不傳遞任何引數。

rl.on('SIGINT', () => {
  rl.question('Are you sure you want to exit? ', (answer) => {
    if (answer.match(/^y(es)?$/i)) rl.pause();
  });
}); 

事件:'SIGTSTP'#

input 流接收到 Ctrl+Z 輸入(通常稱為 SIGTSTP)時,會觸發 'SIGTSTP' 事件。如果在 input 流接收到 SIGTSTP 時沒有註冊 'SIGTSTP' 事件監聽器,Node.js 程序將被髮送到後臺。

當使用 fg(1p) 恢復程式時,將觸發 'pause''SIGCONT' 事件。這些事件可用於恢復 input 流。

如果程序被髮送到後臺之前 input 流已暫停,則不會觸發 'pause''SIGCONT' 事件。

呼叫監聽器函式時不傳遞任何引數。

rl.on('SIGTSTP', () => {
  // This will override SIGTSTP and prevent the program from going to the
  // background.
  console.log('Caught SIGTSTP.');
}); 

'SIGTSTP' 事件在 Windows 上支援。

rl.close()#

rl.close() 方法關閉 InterfaceConstructor 例項並放棄對 inputoutput 流的控制。呼叫時,將觸發 'close' 事件。

呼叫 rl.close() 不會立即停止由 InterfaceConstructor 例項觸發的其他事件(包括 'line')。

rl[Symbol.dispose]()#

rl.close() 的別名。

rl.pause()#

rl.pause() 方法暫停 input 流,允許在必要時稍後恢復。

呼叫 rl.pause() 不會立即暫停由 InterfaceConstructor 例項觸發的其他事件(包括 'line')。

rl.prompt([preserveCursor])#

  • preserveCursor <boolean> 如果為 true,則防止游標位置被重置為 0

rl.prompt() 方法將 InterfaceConstructor 例項配置的 prompt 寫入 output 的新行,以便為使用者提供一個新的位置來輸入。

呼叫時,如果 input 流已暫停,rl.prompt() 將恢復它。

如果建立 InterfaceConstructor 時將 output 設定為 nullundefined,則不會寫入提示。

rl.resume()#

如果 input 流已暫停,rl.resume() 方法會恢復它。

rl.setPrompt(prompt)#

rl.setPrompt() 方法設定在呼叫 rl.prompt() 時將寫入 output 的提示。

rl.getPrompt()#

  • 返回:<string> 當前的提示字串

rl.getPrompt() 方法返回 rl.prompt() 使用的當前提示。

rl.write(data[, key])#

rl.write() 方法將 data 或由 key 標識的鍵序列寫入 output。僅當 outputTTY 文字終端時,才支援 key 引數。有關組合鍵的列表,請參見 TTY 快捷鍵繫結

如果指定了 key,則忽略 data

呼叫時,如果 input 流已暫停,rl.write() 將恢復它。

如果建立 InterfaceConstructor 時將 output 設定為 nullundefined,則不會寫入 datakey

rl.write('Delete this!');
// Simulate Ctrl+U to delete the line written previously
rl.write(null, { ctrl: true, name: 'u' }); 

rl.write() 方法將資料寫入 readline Interfaceinput就好像是使用者提供的一樣

rl[Symbol.asyncIterator]()#

建立一個 AsyncIterator 物件,該物件以字串形式遍歷輸入流中的每一行。此方法允許透過 for await...of 迴圈對 InterfaceConstructor 物件進行非同步迭代。

輸入流中的錯誤不會被轉發。

如果迴圈因 breakthrowreturn 而終止,將呼叫 rl.close()。換句話說,對 InterfaceConstructor 的迭代將始終完全消耗輸入流。

效能不如傳統的 'line' 事件 API。對於效能敏感的應用程式,請改用 'line'

async function processLineByLine() {
  const rl = readline.createInterface({
    // ...
  });

  for await (const line of rl) {
    // Each line in the readline input will be successively available here as
    // `line`.
  }
} 

readline.createInterface() 一旦呼叫就會開始消耗輸入流。在介面建立和非同步迭代之間進行非同步操作可能會導致行丟失。

rl.line#

節點正在處理的當前輸入資料。

當從 TTY 流收集輸入時,可以使用此屬性來檢索在 line 事件觸發之前已處理的當前值。一旦 line 事件被觸發,此屬性將成為一個空字串。

請注意,如果在例項執行時修改該值而不同時控制 rl.cursor,可能會產生意想不到的後果。

如果不使用 TTY 流進行輸入,請使用 'line' 事件。

一個可能的用例如下:

const values = ['lorem ipsum', 'dolor sit amet'];
const rl = readline.createInterface(process.stdin);
const showResults = debounce(() => {
  console.log(
    '\n',
    values.filter((val) => val.startsWith(rl.line)).join(' '),
  );
}, 300);
process.stdin.on('keypress', (c, k) => {
  showResults();
}); 

rl.cursor#

游標相對於 rl.line 的位置。

當從 TTY 流讀取輸入時,這將跟蹤當前游標在輸入字串中的位置。游標的位置決定了在處理輸入時將被修改的輸入字串部分,以及終端游標將呈現的列。

rl.getCursorPos()#

返回游標相對於輸入提示+字串的真實位置。計算中包括長輸入(換行)字串以及多行提示。

Promises API#

類:readlinePromises.Interface#

readlinePromises.Interface 類的例項是使用 readlinePromises.createInterface() 方法構造的。每個例項都與一個 input 可讀流和一個 output 可寫流相關聯。output 流用於為到達 input 流並從中讀取的使用者輸入列印提示。

rl.question(query[, options])#
  • query <string> 要寫入 output 的語句或查詢,前置於提示符。
  • options <Object>
    • signal <AbortSignal> 可選地允許使用 AbortSignal 取消 question()
  • 返回:<Promise> 一個 Promise,它會在使用者響應 query 的輸入時被 fulfill。

rl.question() 方法透過將 query 寫入 output 來顯示它,等待使用者在 input 上提供輸入,然後呼叫 callback 函式,將提供的輸入作為第一個引數傳遞。

呼叫時,如果 input 流已暫停,rl.question() 將恢復它。

如果建立 readlinePromises.Interface 時將 output 設定為 nullundefined,則不會寫入 query

如果在 rl.close() 之後呼叫此問題,它將返回一個被拒絕的 Promise。

用法示例:

const answer = await rl.question('What is your favorite food? ');
console.log(`Oh, so your favorite food is ${answer}`); 

使用 AbortSignal 取消提問。

const signal = AbortSignal.timeout(10_000);

signal.addEventListener('abort', () => {
  console.log('The food question timed out');
}, { once: true });

const answer = await rl.question('What is your favorite food? ', { signal });
console.log(`Oh, so your favorite food is ${answer}`); 

類:readlinePromises.Readline#

new readlinePromises.Readline(stream[, options])#
rl.clearLine(dir)#
  • dir <integer>
    • -1: 從游標向左
    • 1: 從游標向右
    • 0: 整行
  • 返回:this

rl.clearLine() 方法向內部待處理操作列表新增一個操作,該操作以 dir 標識的指定方向清除關聯 stream 的當前行。呼叫 rl.commit() 以檢視此方法的效果,除非在建構函式中傳遞了 autoCommit: true

rl.clearScreenDown()#
  • 返回:this

rl.clearScreenDown() 方法向內部待處理操作列表新增一個操作,該操作從游標當前位置向下清除關聯的流。呼叫 rl.commit() 以檢視此方法的效果,除非在建構函式中傳遞了 autoCommit: true

rl.commit()#

rl.commit() 方法將所有待處理的操作傳送到關聯的 stream,並清除內部的待處理操作列表。

rl.cursorTo(x[, y])#

rl.cursorTo() 方法向內部待處理操作列表新增一個操作,該操作將游標移動到關聯 stream 中的指定位置。呼叫 rl.commit() 以檢視此方法的效果,除非在建構函式中傳遞了 autoCommit: true

rl.moveCursor(dx, dy)#

rl.moveCursor() 方法向內部待處理操作列表新增一個操作,該操作將游標相對於其在關聯 stream 中的當前位置進行移動。呼叫 rl.commit() 以檢視此方法的效果,除非在建構函式中傳遞了 autoCommit: true

rl.rollback()#
  • 返回:this

rl.rollback 方法清除內部的待處理操作列表,而不將其傳送到關聯的 stream

readlinePromises.createInterface(options)#

  • options <Object>
    • input <stream.Readable> 要監聽的可讀流。此選項是必需的
    • output <stream.Writable> 要寫入 readline 資料的可寫流。
    • completer <Function> 一個可選函式,用於 Tab 自動補全。
    • terminal <boolean> 如果 inputoutput 流應被視為 TTY,並且應向其寫入 ANSI/VT100 轉義碼,則為 true預設值:在例項化時檢查 output 流的 isTTY
    • history <string[]> 歷史記錄行的初始列表。此選項僅在使用者或內部 output 檢查將 terminal 設定為 true 時才有意義,否則根本不會初始化歷史記錄快取機制。預設值: []
    • historySize <number> 保留的歷史記錄行的最大數量。要停用歷史記錄,請將此值設定為 0。此選項僅在使用者或內部 output 檢查將 terminal 設定為 true 時才有意義,否則根本不會初始化歷史記錄快取機制。預設值: 30
    • removeHistoryDuplicates <boolean> 如果為 true,當新增到歷史記錄列表的新輸入行與舊行重複時,將從列表中刪除舊行。預設值: false
    • prompt <string> 要使用的提示字串。預設值: '> '
    • crlfDelay <number> 如果 \r\n 之間的延遲超過 crlfDelay 毫秒,則 \r\n 都將被視為單獨的行尾輸入。crlfDelay 將被強制轉換為不小於 100 的數字。它可以設定為 Infinity,在這種情況下,\r 後跟 \n 將始終被視為單個換行符(這對於讀取具有 \r\n 行分隔符的檔案可能是合理的)。預設值: 100
    • escapeCodeTimeout <number> readlinePromises 將等待一個字元的持續時間(以毫秒為單位,當讀取一個模糊的鍵序列時,該序列既可以使用目前為止讀取的輸入形成一個完整的鍵序列,也可以接受額外的輸入來完成一個更長的鍵序列)。預設值: 500
    • tabSize <integer> 一個製表符等於的空格數(最小為 1)。預設值: 8
    • signal <AbortSignal> 允許使用 AbortSignal 關閉介面。
  • 返回:<readlinePromises.Interface>

readlinePromises.createInterface() 方法建立一個新的 readlinePromises.Interface 例項。

import { createInterface } from 'node:readline/promises';
import { stdin, stdout } from 'node:process';
const rl = createInterface({
  input: stdin,
  output: stdout,
});const { createInterface } = require('node:readline/promises');
const rl = createInterface({
  input: process.stdin,
  output: process.stdout,
});

一旦建立了 readlinePromises.Interface 例項,最常見的情況是監聽 'line' 事件:

rl.on('line', (line) => {
  console.log(`Received: ${line}`);
}); 

如果此例項的 terminaltrue,那麼如果 output 流定義了 output.columns 屬性並在列數發生變化時在 output 上觸發 'resize' 事件(當 process.stdout 是 TTY 時會自動執行此操作),則 output 流將獲得最佳相容性。

completer 函式的用法#

completer 函式將使用者輸入的當前行作為引數,並返回一個包含 2 個條目的 Array

  • 一個包含匹配補全條目的 Array
  • 用於匹配的子字串。

例如:[[substr1, substr2, ...], originalsubstring]

function completer(line) {
  const completions = '.help .error .exit .quit .q'.split(' ');
  const hits = completions.filter((c) => c.startsWith(line));
  // Show all completions if none found
  return [hits.length ? hits : completions, line];
} 

completer 函式也可以返回一個 <Promise>,或者可以是非同步的:

async function completer(linePartial) {
  await someAsyncWork();
  return [['123'], linePartial];
} 

回撥 API#

類:readline.Interface#

readline.Interface 類的例項是使用 readline.createInterface() 方法構造的。每個例項都與一個 input 可讀流和一個 output 可寫流相關聯。output 流用於為到達 input 流並從中讀取的使用者輸入列印提示。

rl.question(query[, options], callback)#
  • query <string> 要寫入 output 的語句或查詢,前置於提示符。
  • options <Object>
    • signal <AbortSignal> 可選地允許使用 AbortController 取消 question()
  • callback <Function> 一個回撥函式,在使用者響應 query 的輸入時被呼叫。

rl.question() 方法透過將 query 寫入 output 來顯示它,等待使用者在 input 上提供輸入,然後呼叫 callback 函式,將提供的輸入作為第一個引數傳遞。

呼叫時,如果 input 流已暫停,rl.question() 將恢復它。

如果建立 readline.Interface 時將 output 設定為 nullundefined,則不會寫入 query

傳遞給 rl.question()callback 函式不遵循接受 Error 物件或 null 作為第一個引數的典型模式。該 callback 被呼叫時,只傳入提供的答案作為唯一引數。

如果在 rl.close() 之後呼叫 rl.question(),將會丟擲一個錯誤。

用法示例:

rl.question('What is your favorite food? ', (answer) => {
  console.log(`Oh, so your favorite food is ${answer}`);
}); 

使用 AbortController 取消提問。

const ac = new AbortController();
const signal = ac.signal;

rl.question('What is your favorite food? ', { signal }, (answer) => {
  console.log(`Oh, so your favorite food is ${answer}`);
});

signal.addEventListener('abort', () => {
  console.log('The food question timed out');
}, { once: true });

setTimeout(() => ac.abort(), 10000); 

readline.clearLine(stream, dir[, callback])#

  • stream <stream.Writable>
  • dir <number>
    • -1: 從游標向左
    • 1: 從游標向右
    • 0: 整行
  • callback <Function> 操作完成後呼叫。
  • 返回:<boolean> 如果 stream 希望呼叫程式碼在繼續寫入額外資料之前等待 'drain' 事件觸發,則為 false;否則為 true

readline.clearLine() 方法以 dir 標識的指定方向清除給定的 TTY 流的當前行。

readline.clearScreenDown(stream[, callback])#

  • stream <stream.Writable>
  • callback <Function> 操作完成後呼叫。
  • 返回:<boolean> 如果 stream 希望呼叫程式碼在繼續寫入額外資料之前等待 'drain' 事件觸發,則為 false;否則為 true

readline.clearScreenDown() 方法從游標當前位置向下清除給定的 TTY 流。

readline.createInterface(options)#

  • options <Object>
    • input <stream.Readable> 要監聽的可讀流。此選項是必需的
    • output <stream.Writable> 要寫入 readline 資料的可寫流。
    • completer <Function> 一個可選函式,用於 Tab 自動補全。
    • terminal <boolean> 如果 inputoutput 流應被視為 TTY,並且應向其寫入 ANSI/VT100 轉義碼,則為 true預設值:在例項化時檢查 output 流的 isTTY
    • history <string[]> 歷史記錄行的初始列表。此選項僅在使用者或內部 output 檢查將 terminal 設定為 true 時才有意義,否則根本不會初始化歷史記錄快取機制。預設值: []
    • historySize <number> 保留的歷史記錄行的最大數量。要停用歷史記錄,請將此值設定為 0。此選項僅在使用者或內部 output 檢查將 terminal 設定為 true 時才有意義,否則根本不會初始化歷史記錄快取機制。預設值: 30
    • removeHistoryDuplicates <boolean> 如果為 true,當新增到歷史記錄列表的新輸入行與舊行重複時,將從列表中刪除舊行。預設值: false
    • prompt <string> 要使用的提示字串。預設值: '> '
    • crlfDelay <number> 如果 \r\n 之間的延遲超過 crlfDelay 毫秒,則 \r\n 都將被視為單獨的行尾輸入。crlfDelay 將被強制轉換為不小於 100 的數字。它可以設定為 Infinity,在這種情況下,\r 後跟 \n 將始終被視為單個換行符(這對於讀取具有 \r\n 行分隔符的檔案可能是合理的)。預設值: 100
    • escapeCodeTimeout <number> readline 將等待一個字元的持續時間(以毫秒為單位,當讀取一個模糊的鍵序列時,該序列既可以使用目前為止讀取的輸入形成一個完整的鍵序列,也可以接受額外的輸入來完成一個更長的鍵序列)。預設值: 500
    • tabSize <integer> 一個製表符等於的空格數(最小為 1)。預設值: 8
    • signal <AbortSignal> 允許使用 AbortSignal 關閉介面。中止訊號將在內部呼叫介面上的 close
  • 返回:<readline.Interface>

readline.createInterface() 方法建立一個新的 readline.Interface 例項。

import { createInterface } from 'node:readline';
import { stdin, stdout } from 'node:process';
const rl = createInterface({
  input: stdin,
  output: stdout,
});const { createInterface } = require('node:readline');
const rl = createInterface({
  input: process.stdin,
  output: process.stdout,
});

一旦建立了 readline.Interface 例項,最常見的情況是監聽 'line' 事件:

rl.on('line', (line) => {
  console.log(`Received: ${line}`);
}); 

如果此例項的 terminaltrue,那麼如果 output 流定義了 output.columns 屬性並在列數發生變化時在 output 上觸發 'resize' 事件(當 process.stdout 是 TTY 時會自動執行此操作),則 output 流將獲得最佳相容性。

當使用 stdin 作為輸入建立 readline.Interface 時,程式在收到 EOF 字元之前不會終止。要不等待使用者輸入就退出,請呼叫 process.stdin.unref()

completer 函式的用法#

completer 函式將使用者輸入的當前行作為引數,並返回一個包含 2 個條目的 Array

  • 一個包含匹配補全條目的 Array
  • 用於匹配的子字串。

例如:[[substr1, substr2, ...], originalsubstring]

function completer(line) {
  const completions = '.help .error .exit .quit .q'.split(' ');
  const hits = completions.filter((c) => c.startsWith(line));
  // Show all completions if none found
  return [hits.length ? hits : completions, line];
} 

如果 completer 函式接受兩個引數,則可以非同步呼叫它:

function completer(linePartial, callback) {
  callback(null, [['123'], linePartial]);
} 

readline.cursorTo(stream, x[, y][, callback])#

readline.cursorTo() 方法將游標移動到給定的 TTY stream 中的指定位置。

readline.moveCursor(stream, dx, dy[, callback])#

readline.moveCursor() 方法將游標相對於其在給定的 TTY stream 中的當前位置進行移動。

readline.emitKeypressEvents(stream[, interface])#

readline.emitKeypressEvents() 方法使給定的可讀流開始觸發與接收到的輸入相對應的 'keypress' 事件。

可選地,interface 指定一個 readline.Interface 例項,當檢測到複製貼上的輸入時,將為其停用自動補全。

如果 stream 是一個 TTY,那麼它必須處於原始模式。

如果 input 是一個終端,任何 readline 例項都會在其 input 上自動呼叫此方法。關閉 readline 例項不會停止 input 觸發 'keypress' 事件。

readline.emitKeypressEvents(process.stdin);
if (process.stdin.isTTY)
  process.stdin.setRawMode(true); 

示例:微型 CLI#

以下示例說明了如何使用 readline.Interface 類來實現一個小型命令列介面:

import { createInterface } from 'node:readline';
import { exit, stdin, stdout } from 'node:process';
const rl = createInterface({
  input: stdin,
  output: stdout,
  prompt: 'OHAI> ',
});

rl.prompt();

rl.on('line', (line) => {
  switch (line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log(`Say what? I might have heard '${line.trim()}'`);
      break;
  }
  rl.prompt();
}).on('close', () => {
  console.log('Have a great day!');
  exit(0);
});const { createInterface } = require('node:readline');
const rl = createInterface({
  input: process.stdin,
  output: process.stdout,
  prompt: 'OHAI> ',
});

rl.prompt();

rl.on('line', (line) => {
  switch (line.trim()) {
    case 'hello':
      console.log('world!');
      break;
    default:
      console.log(`Say what? I might have heard '${line.trim()}'`);
      break;
  }
  rl.prompt();
}).on('close', () => {
  console.log('Have a great day!');
  process.exit(0);
});

示例:逐行讀取檔案流#

readline 的一個常見用例是逐行消費一個輸入檔案。最簡單的方法是利用 fs.ReadStream API 以及 for await...of 迴圈:

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

async function processLineByLine() {
  const fileStream = createReadStream('input.txt');

  const rl = createInterface({
    input: fileStream,
    crlfDelay: Infinity,
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();const { createReadStream } = require('node:fs');
const { createInterface } = require('node:readline');

async function processLineByLine() {
  const fileStream = createReadStream('input.txt');

  const rl = createInterface({
    input: fileStream,
    crlfDelay: Infinity,
  });
  // Note: we use the crlfDelay option to recognize all instances of CR LF
  // ('\r\n') in input.txt as a single line break.

  for await (const line of rl) {
    // Each line in input.txt will be successively available here as `line`.
    console.log(`Line from file: ${line}`);
  }
}

processLineByLine();

或者,可以使用 'line' 事件:

import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

const rl = createInterface({
  input: createReadStream('sample.txt'),
  crlfDelay: Infinity,
});

rl.on('line', (line) => {
  console.log(`Line from file: ${line}`);
});const { createReadStream } = require('node:fs');
const { createInterface } = require('node:readline');

const rl = createInterface({
  input: createReadStream('sample.txt'),
  crlfDelay: Infinity,
});

rl.on('line', (line) => {
  console.log(`Line from file: ${line}`);
});

目前,for await...of 迴圈可能會慢一些。如果 async / await 流程和速度都至關重要,可以採用混合方法:

import { once } from 'node:events';
import { createReadStream } from 'node:fs';
import { createInterface } from 'node:readline';

(async function processLineByLine() {
  try {
    const rl = createInterface({
      input: createReadStream('big-file.txt'),
      crlfDelay: Infinity,
    });

    rl.on('line', (line) => {
      // Process the line.
    });

    await once(rl, 'close');

    console.log('File processed.');
  } catch (err) {
    console.error(err);
  }
})();const { once } = require('node:events');
const { createReadStream } = require('node:fs');
const { createInterface } = require('node:readline');

(async function processLineByLine() {
  try {
    const rl = createInterface({
      input: createReadStream('big-file.txt'),
      crlfDelay: Infinity,
    });

    rl.on('line', (line) => {
      // Process the line.
    });

    await once(rl, 'close');

    console.log('File processed.');
  } catch (err) {
    console.error(err);
  }
})();

TTY 快捷鍵繫結#

快捷鍵 描述 注意
Ctrl+Shift+Backspace 刪除左側行 在 Linux、Mac 和 Windows 上無效
Ctrl+Shift+Delete 刪除右側行 在 Mac 上無效
Ctrl+C 觸發 SIGINT 或關閉 readline 例項
Ctrl+H 向左刪除
Ctrl+D 向右刪除,或在當前行空/EOF時關閉 readline 例項 在 Windows 上無效
Ctrl+U 從當前位置刪除到行首
Ctrl+K 從當前位置刪除到行尾
Ctrl+Y 貼上(恢復)之前刪除的文字 僅對透過 Ctrl+UCtrl+K 刪除的文字有效
Meta+Y 在先前刪除的文字之間迴圈 僅在最後一次按鍵是 Ctrl+YMeta+Y 時可用
Ctrl+A 移動到行首
Ctrl+E 移動到行尾
Ctrl+B 後退一個字元
Ctrl+F 前進一個字元
Ctrl+L 清屏
Ctrl+N 下一個歷史記錄項
Ctrl+P 上一個歷史記錄項
Ctrl+- 撤銷上一次更改 任何發出鍵碼 0x1F 的按鍵都會執行此操作。在許多終端(例如 xterm)中,這被繫結到 Ctrl+-
Ctrl+6 重做上一次更改 許多終端沒有預設的重做快捷鍵。我們選擇鍵碼 0x1E 來執行重做。在 xterm 中,它預設繫結到 Ctrl+6
Ctrl+Z 將正在執行的程序移至後臺。輸入 fg 並按 Enter 返回。 在 Windows 上無效
Ctrl+WCtrl +Backspace 向後刪除到單詞邊界 Ctrl+Backspace 在 Linux、Mac 和 Windows 上無效
Ctrl+Delete 向前刪除到單詞邊界 在 Mac 上無效
Ctrl+Left arrowMeta+B 向左移動一個單詞 Ctrl+Left arrow 在 Mac 上無效
Ctrl+Right arrowMeta+F 向右移動一個單詞 Ctrl+Right arrow 在 Mac 上無效
Meta+DMeta +Delete 向右刪除一個單詞 Meta+Delete 在 Windows 上無效
Meta+Backspace 向左刪除一個單詞 在 Mac 上無效