Skip to content

Readline

[稳定版: 2 - 稳定版]

稳定版: 2 稳定性: 2 - 稳定版

源代码: lib/readline.js

node:readline 模块提供了一个接口,用于逐行读取来自 可读 流(例如 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: 将答案记录到数据库中
  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 可读 流和单个 output 可写 流关联。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' 事件。这通常发生在用户按下 Enter 键 或 Return 键 时。

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

监听器函数将使用包含接收到的单行输入的字符串调用。

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

事件:'history'

新增于:v15.8.0, v14.18.0

每当历史数组发生变化时,就会发出 'history' 事件。

监听器函数将使用包含历史数组的数组调用。它将反映所有更改,包括由于 historySizeremoveHistoryDuplicates 而添加的行和删除的行。

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

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 resumed.')
})

事件:'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('Are you sure you want to exit? ', 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('Caught SIGTSTP.')
})

Windows 系统不支持 'SIGTSTP' 事件。

rl.close()

新增于:v0.1.98

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

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

rl.pause()

新增于:v0.3.4

rl.pause() 方法暂停 input 流,如有必要,稍后可以恢复它。

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

rl.prompt([preserveCursor])

新增于:v0.1.98

  • preserveCursor <布尔值> 如果为 true,则阻止光标位置重置为 0

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

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

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

rl.resume()

新增于:v0.3.4

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

rl.setPrompt(prompt)

新增于:v0.1.98

rl.setPrompt() 方法设置提示符,每当调用 rl.prompt() 时,该提示符将被写入 output

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

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

如果使用 output 设置为 nullundefined 创建 InterfaceConstructor,则不会写入 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值将始终为字符串,永不为未定义。
v0.1.98新增于:v0.1.98

Node.js 正在处理的当前输入数据。

这可以用于从 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

  • 返回值:<Object>
    • rows <number> 光标当前所在的提示行的行号
    • cols <number> 光标当前所在的屏幕列号

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

Promise API

新增于:v17.0.0

[稳定版:1 - 实验性]

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

类:readlinePromises.Interface

新增于:v17.0.0

readlinePromises.Interface 类的实例使用 readlinePromises.createInterface() 方法构造。每个实例都与单个 input 可读 流和单个 output 可写 流关联。output 流用于打印用户输入的提示,这些输入来自 input 流并从中读取。

rl.question(query[, options])

新增于:v17.0.0

  • query <string> 写入 output 的语句或查询,作为提示的前缀。

  • options <Object>

    • signal <AbortSignal> 可选地允许使用 AbortSignal 取消 question()
  • 返回: <Promise> 一个 promise,当用户响应 query 输入时,该 promise 将被 fulfilled。

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

调用时,rl.question() 将恢复已暂停的 input 流。

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

如果在 rl.close() 后调用 question,则返回一个 rejected 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}`)

类: readlinePromises.Readline

新增于: v17.0.0

new readlinePromises.Readline(stream[, options])

新增于: v17.0.0

rl.clearLine(dir)

新增于: v17.0.0

  • dir <integer>
    • -1: 从光标左侧
    • 1: 从光标右侧
    • 0: 整行
  • 返回值: this

rl.clearLine() 方法向内部待处理操作列表中添加一个操作,该操作会根据 dir 指定的方向清除关联 stream 的当前行。除非构造函数中传递了 autoCommit: true,否则需要调用 rl.commit() 才能看到此方法的效果。

rl.clearScreenDown()

新增于: v17.0.0

  • 返回值: this

rl.clearScreenDown() 方法向内部待处理操作列表添加一个操作,该操作将清除关联流中从光标当前位置到末尾的内容。除非在构造函数中传递了 autoCommit: true,否则需要调用 rl.commit() 才能看到此方法的效果。

rl.commit()

新增于: v17.0.0

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

rl.cursorTo(x[, y])

新增于: v17.0.0

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

rl.moveCursor(dx, dy)

新增于: v17.0.0

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

rl.rollback()

新增于: v17.0.0

  • 返回值: this

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

readlinePromises.createInterface(options)

新增于: v17.0.0

  • options <对象>

    • input <stream.Readable> 要监听的 可读 流。此选项为 必填
    • output <stream.Writable> 用于写入 readline 数据的 可写 流。
    • completer <函数> 用于 Tab 自动完成的可选函数。
    • terminal <布尔值> 如果 inputoutput 流应该像 TTY 一样处理,并向其写入 ANSI/VT100 转义码,则为 true默认值: 实例化时检查 output 流上的 isTTY
    • history <字符串数组> 历史行初始列表。只有当用户或内部 output 检查将 terminal 设置为 true 时,此选项才有意义,否则根本不会初始化历史缓存机制。默认值: []
    • historySize <数字> 保留的历史行最大数量。要禁用历史记录,请将此值设置为 0。只有当用户或内部 output 检查将 terminal 设置为 true 时,此选项才有意义,否则根本不会初始化历史缓存机制。默认值: 30
    • removeHistoryDuplicates <布尔值> 如果为 true,则当添加到历史列表的新输入行重复了旧的行时,这将从列表中删除旧的行。默认值: false
    • prompt <字符串> 要使用的提示字符串。默认值: '\> '
    • crlfDelay <数字> 如果 \r\n 之间的延迟超过 crlfDelay 毫秒,则 \r\n 都将被视为单独的行尾输入。crlfDelay 将被强制转换为不小于 100 的数字。它可以设置为 Infinity,在这种情况下,\r 后跟 \n 将始终被视为单个换行符(这对于使用 \r\n 行分隔符读取文件 可能比较合理)。默认值: 100
    • escapeCodeTimeout <数字> readlinePromises 将等待一个字符的持续时间(以毫秒为单位,读取模棱两可的键序列时,该键序列可以使用到目前为止读取的输入形成完整的键序列,并且可以接受其他输入来完成更长的键序列)。默认值: 500
    • tabSize <整数> 一个制表符等于的空格数(至少 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 可读 流和单个 output 可写 流关联。output 流用于打印用户输入的提示,这些输入到达并从 input 流读取。

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

新增于: v0.3.3

  • query <string> 写入 output 的语句或查询,作为提示的前缀。

  • options <Object>

    • signal <AbortSignal> 可选,允许使用 AbortController 取消 question()
  • callback <Function> 一个回调函数,在接收到用户针对 query 的输入后被调用。

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

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

如果使用 output 设置为 nullundefined 创建 readline.Interface,则不会写入 query

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

如果在 rl.close() 后调用 rl.question(),则会抛出错误。

示例用法:

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

使用 AbortController 取消问题。

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

[历史]

版本变更
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 时会自动执行此操作)。

使用 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 实例,当检测到复制粘贴输入时,其自动完成功能将被禁用。

如果 stream 是一个 TTY,则它必须处于原始模式。

任何 readline 实例都会在其 input 上自动调用此方法(如果 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 from file: ${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 from file: ${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 from file: ${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 from file: ${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 键绑定

键绑定描述备注
Ctrl + Delete删除左侧一行不适用于 Linux、Mac 和 Windows
Ctrl + D删除右侧一行不适用于 Mac
Ctrl + C发送 SIGINT 信号或关闭 readline 实例
Ctrl + Backspace删除左侧字符
Ctrl + Delete删除右侧字符或如果当前行为空/EOF 则关闭 readline 实例不适用于 Windows
Alt + Backspace从当前位置删除到行首
Alt + Delete从当前位置删除到行尾
Alt + Y粘贴 (恢复) 上一次删除的文本只适用于通过 Ctrl + Backspace 或 Ctrl + Delete 删除的文本
Alt + ]在之前删除的文本之间循环仅当最后一个按键是 Ctrl + Backspace 或 Ctrl + Delete 时可用
Home前往行首
End前往行尾
左箭头向左移动一个字符
右箭头向右移动一个字符
Ctrl + L清屏
上箭头下一个历史记录项
下箭头上一个历史记录项
Ctrl + _撤销上一次更改任何发出键码 0x1F 的按键都会执行此操作。在许多终端中,例如 xterm,这绑定到 Ctrl + _。
Ctrl + Shift + Z重做上一次更改许多终端没有默认的重做按键。我们选择键码 0x1E 来执行重做。在 xterm 中,它默认绑定到 Ctrl + Shift + Z。
Ctrl + Z将运行中的进程移动到后台。键入 fg 并按 Enter 返回。不适用于 Windows
Alt + Backspace 或 Ctrl + w向后删除到字边界Ctrl + w 不适用于 Linux、Mac 和 Windows
Alt + Delete向前删除到字边界不适用于 Mac
Alt + 左箭头 或 Ctrl + 左箭头向左移动一个单词Ctrl + 左箭头 不适用于 Mac
Alt + 右箭头 或 Ctrl + 右箭头向右移动一个单词Ctrl + 右箭头 不适用于 Mac
Alt + Delete 或 Ctrl + Delete删除右侧单词Ctrl + Delete 不适用于 Windows
Alt + Backspace删除左侧单词不适用于 Mac