Readline#

稳定性:2 - 稳定

源代码: lib/readline.js

node:readline 模块提供了一个接口,用于从 Readable 流(例如 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 Readable 流和单个 output Writable 流关联。 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' 事件。 这通常发生在用户按下 EnterReturn 时。

如果已从流中读取新数据,并且该流在没有最终行尾标记的情况下结束,也会触发 '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'#

当之前使用 Ctrl+Z(即 SIGTSTP)移到后台的 Node.js 进程然后使用 fg(1p) 带回前台时,会触发 'SIGCONT' 事件。

如果 input 流在 SIGTSTP 请求之前暂停,则不会触发此事件。

调用监听器函数时不传递任何参数。

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

Windows 上支持 'SIGCONT' 事件。

事件:'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.');
}); 

Windows 上支持 'SIGTSTP' 事件。

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() 将恢复该流。

如果使用设置为 nullundefinedoutput 创建了 InterfaceConstructor,则不会写入提示符。

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() 方法会将 datakey 标识的键序列写入到 output。 仅当 outputTTY 文本终端时,才支持 key 参数。 有关键组合的列表,请参见 TTY 键绑定

如果指定了 key,则忽略 data

调用时,如果 input 流已暂停,则 rl.write() 将恢复该流。

如果使用将 output 设置为 nullundefined 创建了 InterfaceConstructor,则不会写入 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 Readable 流和单个 output Writable 流相关联。 output 流用于打印提示符,以提示用户在 input 流上到达并从该流读取的输入。

rl.question(query[, options])#
  • query <string> 要写入到 output 的语句或查询,添加到提示符之前。
  • options <Object>
    • signal <AbortSignal> 可选地允许使用 AbortSignal 取消 question()
  • 返回值: <Promise> 一个 promise,它将以用户响应 query 的输入来实现。

rl.question() 方法通过将其写入 output 来显示 query,等待用户在 input 上提供输入,然后调用 callback 函数,将提供的输入作为第一个参数传递。

调用时,如果 input 流已暂停,则 rl.question() 将恢复该流。

如果使用将 output 设置为 nullundefined 创建了 readlinePromises.Interface,则不会写入 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> 要监听的 Readable 流。此选项为必需
    • output <stream.Writable> 用于将 readline 数据写入的 Writable 流。
    • 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
  • 返回:<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 时会自动执行此操作),则该流将获得最佳兼容性。

使用 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 Readable 流和单个 output Writable 流相关联。 output 流用于打印用户输入的提示,这些提示到达并从 input 流中读取。

rl.question(query[, options], callback)#
  • query <string> 要写入到 output 的语句或查询,添加到提示符之前。
  • options <Object>
    • signal <AbortSignal> (可选)允许使用 AbortController 取消 question()
  • callback <Function> (函数) 一个回调函数,该函数被调用,并将用户的输入作为对 query 的响应。

rl.question() 方法通过将其写入 output 来显示 query,等待用户在 input 上提供输入,然后调用 callback 函数,将提供的输入作为第一个参数传递。

调用时,如果 input 流已暂停,则 rl.question() 将恢复该流。

如果使用 output 设置为 nullundefined 创建了 readline.Interface,则不写入 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() 方法清除给定 TTY 流中由 dir 标识的指定方向的当前行。

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> 要监听的 Readable 流。此选项为必需
    • output <stream.Writable> 用于将 readline 数据写入的 Writable 流。
    • 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 时会自动执行此操作),则该流将获得最佳兼容性。

当使用 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])#

  • stream <stream.Writable>
  • x <number>
  • y <number>
  • callback <Function> (函数) 操作完成后调用。
  • 返回:<boolean> (布尔值)如果 stream 希望调用代码在继续写入其他数据之前等待发出 'drain' 事件,则为 false;否则为 true

readline.cursorTo() 方法将光标移动到给定的 TTY stream 中的指定位置。

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

  • stream <stream.Writable>
  • dx <number>
  • dy <number>
  • callback <Function> (函数) 操作完成后调用。
  • 返回:<boolean> (布尔值)如果 stream 希望调用代码在继续写入其他数据之前等待发出 'drain' 事件,则为 false;否则为 true

readline.moveCursor() 方法将光标*相对于*它在给定的 TTY stream 中的当前位置移动。

readline.emitKeypressEvents(stream[, interface])#

readline.emitKeypressEvents() 方法使给定的 Readable 流开始发出与接收到的输入相对应的 '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 Yank(调用)先前删除的文本 仅适用于由 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 上不起作用