Skip to content

Readline

[稳定度: 2 - 稳定]

稳定度: 2 Stability: 2 - 稳定

源码: lib/readline.js

node:readline 模块提供了一个接口,用于一次一行地从 Readable 流(例如 process.stdin)读取数据。

要使用基于 Promise 的 API:

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

要使用回调和同步 API:

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

以下简单示例说明了 node:readline 模块的基本用法。

js
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();
js
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

加入于: v0.1.104

InterfaceConstructor 类的实例使用 readlinePromises.createInterface()readline.createInterface() 方法构造。 每个实例都与单个 input Readable 流和单个 output Writable 流相关联。 output 流用于打印提示,以提示用户在 input 流上接收并读取的输入。

事件: 'close'

加入版本: v0.1.98

当发生以下情况之一时,会触发 'close' 事件:

  • 调用了 rl.close() 方法,并且 InterfaceConstructor 实例已放弃对 inputoutput 流的控制;
  • input 流接收到其 'end' 事件;
  • input 流接收到 + 以表示传输结束 (EOT);
  • input 流接收到 + 以表示 SIGINT,并且 InterfaceConstructor 实例上没有注册 'SIGINT' 事件监听器。

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

一旦触发 'close' 事件,InterfaceConstructor 实例就完成了。

事件: 'line'

加入版本: v0.1.98

每当 input 流接收到行尾输入 (\n\r\r\n) 时,就会触发 'line' 事件。 这通常发生在用户按下 或 时。

如果从流中读取了新数据,并且该流在没有最终行尾标记的情况下结束,也会触发 'line' 事件。

监听器函数在调用时会传递一个字符串,其中包含接收到的单行输入。

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

事件: 'history'

加入版本: v15.8.0, v14.18.0

每当 history 数组发生更改时,就会触发 'history' 事件。

监听器函数在调用时会传递一个数组,其中包含 history 数组。 它将反映所有更改,由于 historySizeremoveHistoryDuplicates 而添加的行和删除的行。

主要目的是允许监听器持久化 history。 监听器也可以更改 history 对象。 这对于防止将某些行(如密码)添加到 history 中可能很有用。

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

事件: 'pause'

加入版本: v0.7.5

当发生以下情况之一时,会触发 'pause' 事件:

  • input 流已暂停。
  • input 流未暂停并接收到 'SIGCONT' 事件。(请参阅事件 'SIGTSTP''SIGCONT'。)

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

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

事件: 'resume'

添加于: v0.7.5

每当 input 流恢复时,会触发 'resume' 事件。

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

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

事件: 'SIGCONT'

添加于: v0.7.5

当一个先前使用 + (即 SIGTSTP) 移至后台的 Node.js 进程,之后通过 fg(1p) 带回前台时,会触发 'SIGCONT' 事件。

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

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

js
rl.on('SIGCONT', () => {
  // `prompt` 会自动恢复流
  rl.prompt();
});

Windows 上支持 'SIGCONT' 事件。

事件: 'SIGINT'

添加于: v0.3.0

每当 input 流接收到一个 输入(通常称为 SIGINT)时,会触发 'SIGINT' 事件。 如果在 input 流收到 SIGINT 时,没有注册 'SIGINT' 事件的监听器,则会触发 'pause' 事件。

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

js
rl.on('SIGINT', () => {
  rl.question('确定要退出吗?', (answer) => {
    if (answer.match(/^y(es)?$/i)) rl.pause();
  });
});

事件: 'SIGTSTP'

添加于: v0.7.5

input 流接收到 + 输入(通常称为 SIGTSTP)时,会触发 'SIGTSTP' 事件。 如果在 input 流收到 SIGTSTP 时,没有注册 'SIGTSTP' 事件的监听器,则 Node.js 进程将被发送到后台。

当使用 fg(1p) 恢复程序时,将触发 'pause''SIGCONT' 事件。 这些可用于恢复 input 流。

如果在进程发送到后台之前 input 被暂停,则不会触发 'pause''SIGCONT' 事件。

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

js
rl.on('SIGTSTP', () => {
  // 这将覆盖 SIGTSTP 并阻止程序进入后台。
  console.log('捕获到 SIGTSTP。');
});

Windows 上支持 'SIGTSTP' 事件。

rl.close()

新增于: v0.1.98

rl.close() 方法关闭 InterfaceConstructor 实例,并放弃对 inputoutput 流的控制。 调用后,将触发 'close' 事件。

调用 rl.close() 不会立即停止 InterfaceConstructor 实例触发的其他事件(包括 'line')。

rl.pause()

新增于: v0.3.4

rl.pause() 方法暂停 input 流,允许在必要时稍后恢复。

调用 rl.pause() 不会立即暂停 InterfaceConstructor 实例触发的其他事件(包括 'line')。

rl.prompt([preserveCursor])

新增于: v0.1.98

  • preserveCursor <boolean> 如果为 true,则防止光标位置重置为 0

rl.prompt() 方法将 InterfaceConstructor 实例配置的 prompt 写入 output 中的新行,以便为用户提供一个新的位置来提供输入。

调用后,如果 input 流已暂停,则 rl.prompt() 将恢复它。

如果创建 InterfaceConstructor 时将 output 设置为 nullundefined,则不会写入提示。

rl.resume()

新增于: v0.3.4

如果 input 流已暂停,则 rl.resume() 方法会恢复它。

rl.setPrompt(prompt)

新增于: v0.1.98

rl.setPrompt() 方法设置将写入 output 的提示,只要调用 rl.prompt()

rl.getPrompt()

新增于: v15.3.0, v14.17.0

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

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

rl.write(data[, key])

新增于: v0.1.98

rl.write() 方法会将 data 或由 key 标识的键序列写入 output。 仅当 outputTTY 文本终端时,才支持 key 参数。 有关键组合的列表,请参见 TTY 键绑定

如果指定了 key,则忽略 data

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

如果创建 InterfaceConstructor 时将 output 设置为 nullundefined,则不会写入 datakey

js
rl.write('删除这个!');
// 模拟 Ctrl+U 删除先前写入的行
rl.write(null, { ctrl: true, name: 'u' });

rl.write() 方法会将数据写入 readline Interfaceinput就像它是由用户提供的一样

rl[Symbol.asyncIterator]()

[历史]

版本变更
v11.14.0, v10.17.0Symbol.asyncIterator 支持不再是实验性的。
v11.4.0, v10.16.0加入于: v11.4.0, v10.16.0

创建一个 AsyncIterator 对象,该对象将输入流中的每一行作为一个字符串进行迭代。此方法允许通过 for await...of 循环异步迭代 InterfaceConstructor 对象。

输入流中的错误不会被转发。

如果循环以 breakthrowreturn 终止,则会调用 rl.close()。换句话说,迭代 InterfaceConstructor 将始终完全消耗输入流。

性能不如传统的 'line' 事件 API。对于对性能敏感的应用程序,请改用 'line'

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

  for await (const line of rl) {
    // readline 输入中的每一行都会依次作为 `line` 在这里提供。
  }
}

readline.createInterface() 将在调用后开始消耗输入流。在接口创建和异步迭代之间进行异步操作可能会导致遗漏行。

rl.line

[历史]

版本变更
v15.8.0, v14.18.0值将始终是一个字符串,永远不会是 undefined。
v0.1.98加入于: v0.1.98

node 当前正在处理的输入数据。

当从 TTY 流收集输入时,可以使用它来检索到目前为止已处理的当前值,在 line 事件发出之前。一旦 line 事件被发出,这个属性将是一个空字符串。

请注意,如果在实例运行时修改该值,如果 rl.cursor 没有被控制,可能会产生意想不到的后果。

如果不使用 TTY 流作为输入,请使用 'line' 事件。

一个可能的用例如下:

js
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

添加于: v0.1.98

光标相对于 rl.line 的位置。

当从 TTY 流读取输入时,这将跟踪当前光标在输入字符串中的位置。光标的位置决定了输入字符串中将被修改的部分,以及终端插入符号将被渲染的列。

rl.getCursorPos()

添加于: v13.5.0, v12.16.0

返回光标相对于输入提示符 + 字符串的实际位置。 长输入(换行)字符串以及多行提示符都包含在计算中。

Promises API

添加于: v17.0.0

[稳定度: 1 - 实验性]

稳定度: 1 稳定度: 1 - 实验性

类: readlinePromises.Interface

添加于: v17.0.0

readlinePromises.Interface 类的实例使用 readlinePromises.createInterface() 方法构造。 每个实例都与一个 input Readable 流和一个 output Writable 流相关联。 output 流用于打印提示用户输入,这些输入会到达并从 input 流读取。

rl.question(query[, options])

Added in: v17.0.0

  • query <string> 要写入到 output 的语句或查询,位于提示符之前。

  • options <Object>

    • signal <AbortSignal> 可选,允许使用 AbortSignal 取消 question()
  • 返回: <Promise> 一个 promise,其值是用户对 query 的输入的响应。

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

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

如果 readlinePromises.Interface 创建时 output 设置为 nullundefined,则不写入 query

如果在 rl.close() 之后调用问题,它将返回一个被拒绝的 promise。

用法示例:

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

使用 AbortSignal 取消一个问题。

js
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}`);

Class: readlinePromises.Readline

Added in: v17.0.0

new readlinePromises.Readline(stream[, options])

Added in: v17.0.0

rl.clearLine(dir)

Added in: v17.0.0

  • dir <integer>

    • -1: 光标左侧
    • 1: 光标右侧
    • 0: 整行
  • 返回: this

rl.clearLine() 方法向待执行的内部操作列表添加一个操作,该操作清除与 stream 关联的当前行,清除的方向由 dir 指定。 调用 rl.commit() 来查看此方法的效果,除非在构造函数中传递了 autoCommit: true

rl.clearScreenDown()

Added in: v17.0.0

  • 返回: this

rl.clearScreenDown() 方法向待执行的内部操作列表添加一个操作,该操作清除与流关联的光标当前位置下方的屏幕。 调用 rl.commit() 来查看此方法的效果,除非在构造函数中传递了 autoCommit: true

rl.commit()

Added in: v17.0.0

rl.commit() 方法将所有待执行的操作发送到关联的 stream 并清除待执行操作的内部列表。

rl.cursorTo(x[, y])

Added in: v17.0.0

rl.cursorTo() 方法向待执行的内部操作列表添加一个操作,该操作将光标移动到关联 stream 中的指定位置。 调用 rl.commit() 来查看此方法的效果,除非在构造函数中传递了 autoCommit: true

rl.moveCursor(dx, dy)

Added in: v17.0.0

rl.moveCursor() 方法向待执行的内部操作列表添加一个操作,该操作将光标相对于其在关联 stream 中的当前位置进行移动。 调用 rl.commit() 来查看此方法的效果,除非在构造函数中传递了 autoCommit: true

rl.rollback()

新增于: v17.0.0

  • 返回: this

rl.rollback 方法清除内部的待处理操作列表,而不将其发送到关联的 stream

readlinePromises.createInterface(options)

新增于: v17.0.0

  • 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 实例。

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

创建 readlinePromises.Interface 实例后,最常见的情况是监听 'line' 事件:

js
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]

js
function completer(line) {
  const completions = '.help .error .exit .quit .q'.split(' ');
  const hits = completions.filter((c) => c.startsWith(line));
  // 如果未找到匹配项,则显示所有补全项
  return [hits.length ? hits : completions, line];
}

completer 函数也可以返回一个 <Promise>,或者可以是异步的:

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

回调 API

加入版本: v0.1.104

类: readline.Interface

[历史]

版本变更
v17.0.0readline.Interface 现在继承自 Interface
v0.1.104加入版本: v0.1.104

readline.Interface 类的实例使用 readline.createInterface() 方法构造。 每个实例都与一个 input Readable 流和一个 output Writable 流相关联。 output 流用于打印提示以获取用户输入,这些输入到达 input 流并从其中读取。

rl.question(query[, options], callback)

加入版本: v0.3.3

  • query <string> 要写入 output 的语句或查询,前置于提示符。

  • options <Object>

    • signal <AbortSignal> 可选地允许使用 AbortController 取消 question()
  • callback <Function> 一个回调函数,在使用用户输入响应 query 时调用。

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

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

如果 readline.Interface 是使用设置为 nullundefinedoutput 创建的,则不会写入 query

传递给 rl.question()callback 函数不遵循接受 Error 对象或 null 作为第一个参数的典型模式。 callback 被调用时,只将提供的答案作为参数。

如果在 rl.close() 之后调用 rl.question(),将抛出一个错误。

使用示例:

js
rl.question('你最喜欢的食物是什么?', (answer) => {
  console.log(`哦,所以你最喜欢的食物是 ${answer}`);
});

使用 AbortController 取消问题。

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

rl.question('你最喜欢的食物是什么?', { signal }, (answer) => {
  console.log(`哦,所以你最喜欢的食物是 ${answer}`);
});

signal.addEventListener('abort', () => {
  console.log('食物问题超时了');
}, { once: true });

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

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

[历史]

版本变更
v18.0.0现在,将无效的回调传递给 callback 参数会抛出 ERR_INVALID_ARG_TYPE 而不是 ERR_INVALID_CALLBACK
v12.7.0公开了流的 write() 回调和返回值。
v0.7.7添加于: v0.7.7
  • stream <stream.Writable>

  • dir <number>

    • -1: 光标左侧
    • 1: 光标右侧
    • 0: 整行
  • callback <Function> 操作完成后调用。

  • 返回: <boolean> 如果 stream 希望调用代码等待发出 'drain' 事件后再继续写入其他数据,则为 false;否则为 true

readline.clearLine() 方法清除给定 TTY 流中指定方向上光标所在的当前行,由 dir 标识。

readline.clearScreenDown(stream[, callback])

[历史]

版本变更
v18.0.0现在,将无效的回调传递给 callback 参数会抛出 ERR_INVALID_ARG_TYPE 而不是 ERR_INVALID_CALLBACK
v12.7.0公开了流的 write() 回调和返回值。
v0.7.7添加于: v0.7.7
  • stream <stream.Writable>
  • callback <Function> 操作完成后调用。
  • 返回: <boolean> 如果 stream 希望调用代码等待发出 'drain' 事件后再继续写入其他数据,则为 false;否则为 true

readline.clearScreenDown() 方法清除给定 TTY 流中从光标当前位置向下的内容。

readline.createInterface(options)

[历史]

版本变更
v15.14.0, v14.18.0现在支持 signal 选项。
v15.8.0, v14.18.0现在支持 history 选项。
v13.9.0现在支持 tabSize 选项。
v8.3.0, v6.11.4移除 crlfDelay 选项的最大限制。
v6.6.0现在支持 crlfDelay 选项。
v6.3.0现在支持 prompt 选项。
v6.0.0historySize 选项现在可以为 0
v0.1.98添加于:v0.1.98
  • 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 实例。

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

创建 readline.Interface 实例后,最常见的情况是监听 'line' 事件:

js
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]

js
function completer(line) {
  const completions = '.help .error .exit .quit .q'.split(' ');
  const hits = completions.filter((c) => c.startsWith(line));
  // 如果未找到任何补全项,则显示所有补全项
  return [hits.length ? hits : completions, line];
}

如果 completer 函数接受两个参数,则可以异步调用它:

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

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

[历史]

版本变更
v18.0.0将无效的回调传递给 callback 参数现在会抛出 ERR_INVALID_ARG_TYPE 而不是 ERR_INVALID_CALLBACK
v12.7.0公开了流的 write() 回调和返回值。
v0.7.7添加于:v0.7.7

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

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

[历史]

版本变更
v18.0.0将无效的回调传递给 callback 参数现在会抛出 ERR_INVALID_ARG_TYPE 而不是 ERR_INVALID_CALLBACK
v12.7.0公开了流的 write() 回调和返回值。
v0.7.7添加于:v0.7.7

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

readline.emitKeypressEvents(stream[, interface])

新增于: v0.7.7

readline.emitKeypressEvents() 方法使给定的 Readable 流开始发出与接收到的输入相对应的 'keypress' 事件。

可选地,interface 指定一个 readline.Interface 实例,当检测到复制粘贴的输入时,该实例的自动完成功能将被禁用。

如果 streamTTY,那么它必须处于原始模式。

如果 input 是终端,则任何 readline 实例都会在其 input 上自动调用此方法。 关闭 readline 实例不会停止 input 发出 'keypress' 事件。

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

示例:微型 CLI

以下示例说明了如何使用 readline.Interface 类来实现一个小型命令行接口:

js
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);
});
js
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 循环:

js
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,
  });
  // 注意:我们使用 crlfDelay 选项来识别 input.txt 中所有 CR LF ('\r\n') 实例作为一个换行符。

  for await (const line of rl) {
    // input.txt 中的每一行将在此处连续可用,作为 `line`。
    console.log(`来自文件的行:${line}`);
  }
}

processLineByLine();
js
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,
  });
  // 注意:我们使用 crlfDelay 选项来识别 input.txt 中所有 CR LF ('\r\n') 实例作为一个换行符。

  for await (const line of rl) {
    // input.txt 中的每一行将在此处连续可用,作为 `line`。
    console.log(`来自文件的行:${line}`);
  }
}

processLineByLine();

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

js
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}`);
});
js
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}`);
});

目前,for await...of 循环可能会慢一些。 如果 async / await 流和速度都至关重要,则可以应用混合方法:

js
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) => {
      // 处理该行。
    });

    await once(rl, 'close');

    console.log('文件已处理。');
  } catch (err) {
    console.error(err);
  }
})();
js
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) => {
      // 处理该行。
    });

    await once(rl, 'close');

    console.log('文件已处理。');
  } catch (err) {
    console.error(err);
  }
})();

TTY 键盘绑定

键盘绑定描述备注
+ +删除行左侧内容在 Linux、Mac 和 Windows 上不起作用
+ +删除行右侧内容在 Mac 上不起作用
+发出 SIGINT 或关闭 readline 实例
+删除左侧内容
+删除右侧内容,如果当前行为空/EOF 则关闭 readline 实例在 Windows 上不起作用
+从当前位置删除到行首
+从当前位置删除到行尾
+粘贴(回忆)先前删除的文本仅适用于通过 + 或 + 删除的文本
+在先前删除的文本之间循环仅当上一次按键是 + 或 + 时可用
+转到行首
+转到行尾
+向后移动一个字符
+向前移动一个字符
+清屏
+下一个历史记录项
+上一个历史记录项
+撤消上一次更改任何发出键码 0x1F 的按键都将执行此操作。 例如,在许多终端(如 xterm)中,这绑定到 + 。
+重做上一次更改许多终端没有默认的重做按键。我们选择键码 0x1E 来执行重做。 在 xterm 中,默认情况下它绑定到 + 。
+将正在运行的进程移至后台。 键入 fg 并按 返回。在 Windows 上不起作用
+ 或 +向后删除到单词边界+ 在 Linux、Mac 和 Windows 上不起作用
+向前删除到单词边界在 Mac 上不起作用
+ 或 +左移一个单词+ 在 Mac 上不起作用
+ 或 +右移一个单词+ 在 Mac 上不起作用
+ 或 +删除右侧单词+ 在 Windows 上不起作用
+删除左侧单词在 Mac 上不起作用