Readline
源代码: 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: 将答案记录到数据库中
console.log(`Thank you for your valuable feedback: ${answer}`)
rl.close()
})
一旦执行此代码,Node.js 应用程序将不会终止,直到 readline.Interface
关闭,因为该接口等待在 input
流上接收数据。
类: InterfaceConstructor
新增于: v0.1.104
- 继承自: <EventEmitter>
InterfaceConstructor
类的实例使用 readlinePromises.createInterface()
或 readline.createInterface()
方法构造。每个实例都与单个 input
可读 流和单个 output
可写 流关联。output
流用于打印用户输入的提示,这些输入来自 input
流并从中读取。
事件: 'close'
新增于: v0.1.98
当发生以下情况之一时,将发出 'close'
事件:
- 调用
rl.close()
方法,并且InterfaceConstructor
实例已放弃对input
和output
流的控制; input
流接收到其'end'
事件;input
流接收到 + 以指示传输结束 (EOT);input
流接收到 + 以指示SIGINT
,并且InterfaceConstructor
实例上没有注册'SIGINT'
事件监听器。
监听器函数在不传递任何参数的情况下被调用。
一旦发出 'close'
事件,InterfaceConstructor
实例就完成了。
事件:'line'
新增于:v0.1.98
每当 input
流接收到行尾输入(\n
、\r
或 \r\n
)时,就会发出 'line'
事件。这通常发生在用户按下 Enter 键 或 Return 键 时。
如果从流中读取了新数据,并且该流在没有最终行尾标记的情况下结束,也会发出 'line'
事件。
监听器函数将使用包含接收到的单行输入的字符串调用。
rl.on('line', input => {
console.log(`Received: ${input}`)
})
事件:'history'
新增于:v15.8.0, v14.18.0
每当历史数组发生变化时,就会发出 'history'
事件。
监听器函数将使用包含历史数组的数组调用。它将反映所有更改,包括由于 historySize
和 removeHistoryDuplicates
而添加的行和删除的行。
主要目的是允许监听器持久化历史记录。监听器也可以更改历史对象。这对于防止某些行(例如密码)添加到历史记录中可能很有用。
rl.on('history', history => {
console.log(`Received: ${history}`)
})
事件:'pause'
新增于:v0.7.5
当发生以下情况之一时,将发出 'pause'
事件:
监听器函数调用时不传递任何参数。
rl.on('pause', () => {
console.log('Readline paused.')
})
事件:'resume'
新增于:v0.7.5
每当 input
流恢复时,就会发出 'resume'
事件。
监听器函数调用时不传递任何参数。
rl.on('resume', () => {
console.log('Readline resumed.')
})
事件:'SIGCONT'
新增于:v0.7.5
当使用 +(即 SIGTSTP
)将 Node.js 进程移至后台后,再使用 fg(1p)
将其带回前台时,将发出 'SIGCONT'
事件。
如果 input
流在 SIGTSTP
请求之前被暂停,则不会发出此事件。
监听器函数调用时不传递任何参数。
rl.on('SIGCONT', () => {
// `prompt` 将自动恢复流
rl.prompt()
})
Windows 不支持 'SIGCONT'
事件。
事件: 'SIGINT'
新增于: v0.3.0
当 input
流接收到输入(通常称为 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'
新增于: v0.7.5
当 input
流接收到输入(通常称为 SIGTSTP
)时,将发出 'SIGTSTP'
事件。如果在 input
流接收到 SIGTSTP
时未注册任何 'SIGTSTP'
事件监听器,则 Node.js 进程将被发送到后台。
当使用 fg(1p)
恢复程序时,将发出 'pause'
和 'SIGCONT'
事件。这些事件可用于恢复 input
流。
如果在进程被发送到后台之前 input
已暂停,则不会发出 'pause'
和 'SIGCONT'
事件。
监听器函数调用时不传递任何参数。
rl.on('SIGTSTP', () => {
// 这将覆盖 SIGTSTP 并阻止程序进入后台。
console.log('Caught SIGTSTP.')
})
Windows 系统不支持 'SIGTSTP'
事件。
rl.close()
新增于:v0.1.98
rl.close()
方法关闭 InterfaceConstructor
实例并释放对 input
和 output
流的控制。调用此方法时,将发出 '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
设置为 null
或 undefined
,则不会写入提示符。
rl.resume()
新增于:v0.3.4
rl.resume()
方法恢复 input
流,如果它已被暂停。
rl.setPrompt(prompt)
新增于:v0.1.98
prompt
<string>
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
。只有当 output
是 TTY 文本终端时,才支持 key
参数。有关键组合列表,请参阅 TTY 键绑定。
如果指定了 key
,则忽略 data
。
调用 rl.write()
时,如果 input
流已被暂停,则会恢复它。
如果使用 output
设置为 null
或 undefined
创建 InterfaceConstructor
,则不会写入 data
和 key
。
rl.write('删除此行!')
// 模拟 Ctrl+U 删除先前写入的行
rl.write(null, { ctrl: true, name: 'u' })
rl.write()
方法将数据写入 readline
Interface
的 input
,如同它是由用户提供的。
rl[Symbol.asyncIterator]()
[历史]
版本 | 变更 |
---|---|
v11.14.0, v10.17.0 | Symbol.asyncIterator 支持不再是实验性的。 |
v11.4.0, v10.16.0 | 新增于:v11.4.0, v10.16.0 |
- 返回值:
<AsyncIterator>
创建一个 AsyncIterator
对象,它将输入流中的每一行作为字符串迭代。此方法允许通过 for await...of
循环异步迭代 InterfaceConstructor
对象。
输入流中的错误不会被转发。
如果循环使用 break
、throw
或 return
终止,则将调用 rl.close()
。换句话说,迭代 InterfaceConstructor
将始终完全使用输入流。
性能不如传统的 'line'
事件 API。对于对性能敏感的应用程序,请使用 'line'
。
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'
事件。
一个可能的用例如下所示:
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
返回光标相对于输入提示 + 字符串的实际位置。长的输入(换行)字符串以及多行提示都包含在计算中。
Promise API
新增于:v17.0.0
类: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
设置为 null
或 undefined
,则不会写入 query
。
如果在 rl.close()
后调用 question,则返回一个 rejected 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
新增于: v17.0.0
new readlinePromises.Readline(stream[, options])
新增于: v17.0.0
stream
<stream.Writable> 一个 TTY 流。options
<Object>autoCommit
<boolean> 如果为true
,则无需调用rl.commit()
。
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
- 返回值: <Promise>
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
<布尔值> 如果input
和output
流应该像 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.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}`)
})
如果此实例的 terminal
为 true
,则如果或何时列发生更改,则 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))
// 如果未找到任何匹配项,则显示所有补全项
return [hits.length ? hits : completions, line]
}
completer
函数也可以返回一个 <Promise>,或者为异步函数:
async function completer(linePartial) {
await someAsyncWork()
return [['123'], linePartial]
}
回调 API
新增于:v0.1.104
类:readline.Interface
[历史]
版本 | 变更 |
---|---|
v17.0.0 | readline.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
设置为 null
或 undefined
创建 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])
[历史]
版本 | 变更 |
---|---|
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.0 | historySize 选项现在可以为 0 。 |
v0.1.98 | 新增于:v0.1.98 |
options
<Object>input
<stream.Readable> 要监听的 Readable 流。此选项为 必选。output
<stream.Writable> 用于写入 readline 数据的 Writable 流。completer
<Function> 用于 Tab 自动完成的可选函数。terminal
<boolean> 如果input
和output
流应该被视为 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}`)
})
如果此实例的 terminal
为 true
,则如果或何时列发生更改,则 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))
// 如果没有找到,则显示所有补全项
return [hits.length ? hits : completions, line]
}
如果 completer
函数接受两个参数,则可以异步调用:
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 |
stream
<stream.Writable>x
<number>y
<number>callback
<Function> 操作完成后调用。- 返回值: <boolean> 如果
stream
希望调用代码等待发出'drain'
事件后再继续写入更多数据,则返回false
;否则返回true
。
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 |
stream
<stream.Writable>dx
<number>dy
<number>callback
<Function> 操作完成后调用。- 返回值: <boolean> 如果
stream
希望调用代码等待发出'drain'
事件后再继续写入更多数据,则返回false
;否则返回true
。
readline.moveCursor()
方法相对于给定 TTY stream
中光标的当前位置移动光标。
readline.emitKeypressEvents(stream[, interface])
新增于:v0.7.7
stream
<stream.Readable>interface
<readline.InterfaceConstructor>
readline.emitKeypressEvents()
方法使给定的 Readable 流开始发出与接收到的输入相对应的 'keypress'
事件。
可选地,interface
指定一个 readline.Interface
实例,当检测到复制粘贴输入时,其自动完成功能将被禁用。
如果 stream
是一个 TTY,则它必须处于原始模式。
任何 readline 实例都会在其 input
上自动调用此方法(如果 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,
})
// 注意:我们使用 crlfDelay 选项来识别 input.txt 中所有 CR LF ('\r\n') 实例作为单个换行符。
for await (const line of rl) {
// input.txt 中的每一行都将在此处作为 `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,
})
// 注意:我们使用 crlfDelay 选项来识别 input.txt 中所有 CR LF ('\r\n') 实例作为单个换行符。
for await (const line of rl) {
// input.txt 中的每一行都将在此处作为 `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 => {
// 处理该行。
})
await once(rl, 'close')
console.log('文件处理完成。')
} 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 => {
// 处理该行。
})
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 |