Readline
[稳定度: 2 - 稳定]
稳定度: 2 Stability: 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
加入于: v0.1.104
- 继承: <EventEmitter>
InterfaceConstructor
类的实例使用 readlinePromises.createInterface()
或 readline.createInterface()
方法构造。 每个实例都与单个 input
Readable 流和单个 output
Writable 流相关联。 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'
事件。 这通常发生在用户按下 或 时。
如果从流中读取了新数据,并且该流在没有最终行尾标记的情况下结束,也会触发 'line'
事件。
监听器函数在调用时会传递一个字符串,其中包含接收到的单行输入。
rl.on('line', (input) => {
console.log(`Received: ${input}`);
});
事件: 'history'
加入版本: v15.8.0, v14.18.0
每当 history 数组发生更改时,就会触发 'history'
事件。
监听器函数在调用时会传递一个数组,其中包含 history 数组。 它将反映所有更改,由于 historySize
和 removeHistoryDuplicates
而添加的行和删除的行。
主要目的是允许监听器持久化 history。 监听器也可以更改 history 对象。 这对于防止将某些行(如密码)添加到 history 中可能很有用。
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 恢复.');
});
事件: '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('确定要退出吗?', (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('捕获到 SIGTSTP。');
});
Windows 上不支持 'SIGTSTP'
事件。
rl.close()
新增于: v0.1.98
rl.close()
方法关闭 InterfaceConstructor
实例,并放弃对 input
和 output
流的控制。 调用后,将触发 '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
设置为 null
或 undefined
,则不会写入提示。
rl.resume()
新增于: v0.3.4
如果 input
流已暂停,则 rl.resume()
方法会恢复它。
rl.setPrompt(prompt)
新增于: v0.1.98
prompt
<string>
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
。 仅当 output
是 TTY 文本终端时,才支持 key
参数。 有关键组合的列表,请参见 TTY 键绑定。
如果指定了 key
,则忽略 data
。
调用后,如果 input
流已暂停,则 rl.write()
将恢复它。
如果创建 InterfaceConstructor
时将 output
设置为 null
或 undefined
,则不会写入 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 | 值将始终是一个字符串,永远不会是 undefined。 |
v0.1.98 | 加入于: v0.1.98 |
node 当前正在处理的输入数据。
当从 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
返回光标相对于输入提示符 + 字符串的实际位置。 长输入(换行)字符串以及多行提示符都包含在计算中。
Promises API
添加于: v17.0.0
类: 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
设置为 null
或 undefined
,则不写入 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}`);
Class: readlinePromises.Readline
Added in: v17.0.0
new readlinePromises.Readline(stream[, options])
Added in: v17.0.0
stream
<stream.Writable> 一个 TTY 流。options
<Object>autoCommit
<boolean> 如果为true
,则无需调用rl.commit()
。
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
- 返回: <Promise>
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> 如果应该将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>readlinePromises
将等待字符的持续时间(以毫秒为单位读取不明确的键序列时,该键序列既可以使用到目前为止读取的输入形成完整的键序列,又可以采用其他输入来完成更长的键序列)。 默认值:500
。tabSize
<integer> 一个制表符等于的空格数(最小值为 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
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
是使用设置为 null
或 undefined
的 output
创建的,则不会写入 query
。
传递给 rl.question()
的 callback
函数不遵循接受 Error
对象或 null
作为第一个参数的典型模式。 callback
被调用时,只将提供的答案作为参数。
如果在 rl.close()
之后调用 rl.question()
,将抛出一个错误。
使用示例:
rl.question('你最喜欢的食物是什么?', (answer) => {
console.log(`哦,所以你最喜欢的食物是 ${answer}`);
});
使用 AbortController
取消问题。
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.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.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 时会自动执行此操作),则 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));
// 如果未找到任何补全项,则显示所有补全项
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,那么它必须处于原始模式。
如果 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,
});
// 注意:我们使用 crlfDelay 选项来识别 input.txt 中所有 CR LF ('\r\n') 实例作为一个换行符。
for await (const line of rl) {
// input.txt 中的每一行将在此处连续可用,作为 `line`。
console.log(`来自文件的行:${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}`);
}
}
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}`);
});
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
流和速度都至关重要,则可以应用混合方法:
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 键盘绑定
键盘绑定 | 描述 | 备注 |
---|---|---|
+ + | 删除行左侧内容 | 在 Linux、Mac 和 Windows 上不起作用 |
+ + | 删除行右侧内容 | 在 Mac 上不起作用 |
+ | 发出 SIGINT 或关闭 readline 实例 | |
+ | 删除左侧内容 | |
+ | 删除右侧内容,如果当前行为空/EOF 则关闭 readline 实例 | 在 Windows 上不起作用 |
+ | 从当前位置删除到行首 | |
+ | 从当前位置删除到行尾 | |
+ | 粘贴(回忆)先前删除的文本 | 仅适用于通过 + 或 + 删除的文本 |
+ | 在先前删除的文本之间循环 | 仅当上一次按键是 + 或 + 时可用 |
+ | 转到行首 | |
+ | 转到行尾 | |
+ | 向后移动一个字符 | |
+ | 向前移动一个字符 | |
+ | 清屏 | |
+ | 下一个历史记录项 | |
+ | 上一个历史记录项 | |
+ | 撤消上一次更改 | 任何发出键码 0x1F 的按键都将执行此操作。 例如,在许多终端(如 xterm )中,这绑定到 + 。 |
+ | 重做上一次更改 | 许多终端没有默认的重做按键。我们选择键码 0x1E 来执行重做。 在 xterm 中,默认情况下它绑定到 + 。 |
+ | 将正在运行的进程移至后台。 键入 fg 并按 返回。 | 在 Windows 上不起作用 |
+ 或 + | 向后删除到单词边界 | + 在 Linux、Mac 和 Windows 上不起作用 |
+ | 向前删除到单词边界 | 在 Mac 上不起作用 |
+ 或 + | 左移一个单词 | + 在 Mac 上不起作用 |
+ 或 + | 右移一个单词 | + 在 Mac 上不起作用 |
+ 或 + | 删除右侧单词 | + 在 Windows 上不起作用 |
+ | 删除左侧单词 | 在 Mac 上不起作用 |