Node.js v26.0.0 文档
- Node.js v26.0.0
- 目录
- 逐行读取
- 类:
InterfaceConstructor- 事件:
'close' - 事件:
'error' - 事件:
'line' - 事件:
'history' - 事件:
'pause' - 事件:
'resume' - 事件:
'SIGCONT' - 事件:
'SIGINT' - 事件:
'SIGTSTP' rl.close()rl[Symbol.dispose]()rl.pause()rl.prompt([preserveCursor])rl.resume()rl.setPrompt(prompt)rl.getPrompt()rl.write(data[, key])rl[Symbol.asyncIterator]()rl.linerl.cursorrl.getCursorPos()
- 事件:
- Promises API
- 回调 API
readline.emitKeypressEvents(stream[, interface])- 示例:微型 CLI
- 示例:逐行读取文件流
- TTY 键绑定
- 类:
- 逐行读取
- 索引
- 关于本文档
- 用法与示例
- 断言测试
- 异步上下文跟踪
- 异步钩子
- 缓冲区
- C++ 插件
- 使用 Node-API 的 C/C++ 插件
- C++ 嵌入器 API
- 子进程
- 集群
- 命令行选项
- 控制台
- 加密
- 调试器
- 已弃用的 API
- 诊断通道
- DNS
- 域
- 环境变量
- 错误
- 事件
- 文件系统
- 全局对象
- HTTP
- HTTP/2
- HTTPS
- 检查器
- 国际化
- 模块:CommonJS 模块
- 模块:ECMAScript 模块
- 模块:
node:moduleAPI - 模块:包
- 模块:TypeScript
- 网络
- 可迭代流 API
- 操作系统
- 路径
- 性能钩子
- 权限
- 进程
- Punycode
- 查询字符串
- 逐行读取
- REPL
- 报告
- 单一可执行文件应用
- SQLite
- 流
- 字符串解码器
- 测试运行器
- 定时器
- TLS/SSL
- 跟踪事件
- TTY
- UDP/数据报
- URL
- 实用工具
- V8
- 虚拟机
- WASI
- Web Crypto API
- Web Streams API
- 工作线程
- Zlib
- Zlib 可迭代压缩
- 其他版本
- 选项
Readline#
稳定性:2 - 稳定
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#
- 扩展自:
<EventEmitter>
InterfaceConstructor 类的实例是使用 readlinePromises.createInterface() 或 readline.createInterface() 方法构建的。每个实例都关联有一个单一的 input 可读 流和单一的 output 可写 流。output 流用于打印提示符,以便用户输入到达并从 input 流中读取。
事件:'close'#
当发生以下任一情况时,会触发 'close' 事件:
- 调用了
rl.close()方法,且InterfaceConstructor实例已经放弃了对input和output流的控制; input流接收到其'end'事件;input流接收到 Ctrl+D 以发出传输结束 (EOT) 信号;input流接收到 Ctrl+C 以发出SIGINT信号,且InterfaceConstructor实例上没有注册'SIGINT'事件监听器。
监听器函数被调用时,不传递任何参数。
一旦触发 'close' 事件,InterfaceConstructor 实例即告完成。
事件:'error'#
当关联到 node:readline Interface 的 input 流发生错误时,会触发 'error' 事件。
监听器函数被调用时,将传入一个 Error 对象作为唯一参数。
事件:'line'#
每当 input 流接收到行尾输入(\n、\r 或 \r\n)时,就会触发 'line' 事件。这通常发生用户按下 Enter 或 Return 键时。
如果从流中读取了新数据且流在没有最终行尾标记的情况下结束,也会触发 'line' 事件。
监听器函数被调用时,将传入一个包含接收到的一行输入的字符串。
rl.on('line', (input) => {
console.log(`Received: ${input}`);
});
事件:'history'#
每当历史数组发生变化时,就会触发 'history' 事件。
监听器函数被调用时,传入包含历史记录的数组。它将反映所有变化、添加的行以及由于 historySize 和 removeHistoryDuplicates 而删除的行。
其主要目的是允许监听器持久化历史记录。监听器也可以修改历史对象。这对于防止某些行(如密码)被添加到历史记录中很有用。
rl.on('history', (history) => {
console.log(`Received: ${history}`);
});
事件:'pause'#
当发生以下任一情况时,会触发 'pause' 事件:
监听器函数被调用时,不传递任何参数。
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 实例并放弃对 input 和 output 流的控制。调用时,将触发 '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() 将会恢复它。
如果创建 InterfaceConstructor 时将 output 设置为 null 或 undefined,则不会写入提示符。
rl.resume()#
如果 input 流已被暂停,rl.resume() 方法会将其恢复。
rl.setPrompt(prompt)#
prompt<string>
rl.setPrompt() 方法设置每次调用 rl.prompt() 时写入 output 的提示符。
rl.getPrompt()#
- 返回:
<string>当前提示符字符串
rl.getPrompt() 方法返回 rl.prompt() 使用的当前提示符。
rl.write(data[, key])#
rl.write() 方法会将 data 或 key 标识的按键序列写入 output。仅当 output 是 TTY 文本终端时,才支持 key 参数。有关按键组合的列表,请参见 TTY 键绑定。
如果指定了 key,则忽略 data。
调用时,如果 input 流已暂停,rl.write() 将会恢复它。
如果创建 InterfaceConstructor 时将 output 设置为 null 或 undefined,则不会写入 data 和 key。
rl.write('Delete this!');
// Simulate Ctrl+U to delete the line written previously
rl.write(null, { ctrl: true, name: 'u' });
rl.write() 方法会将数据写入 readline Interface 的 input,就像它是由用户提供的一样。
rl[Symbol.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) {
// Each line in the readline input will be successively available here as
// `line`.
}
}
readline.createInterface() 在调用后将开始消耗输入流。在接口创建和异步迭代之间进行异步操作可能会导致行丢失。
rl.line#
- 类型:
<string>
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#
- 类型:
<number>|<undefined>
相对于 rl.line 的光标位置。
当从 TTY 流读取输入时,这将跟踪当前光标在输入字符串中的落点。光标的位置决定了处理输入时将修改输入字符串的哪一部分,以及终端插入符号将呈现的列。
rl.getCursorPos()#
返回光标相对于输入提示符 + 字符串的实际位置。计算中包含了长输入(自动换行)字符串以及多行提示符。
Promises API#
类:readlinePromises.Interface#
readlinePromises.Interface 类的实例是使用 readlinePromises.createInterface() 方法构建的。每个实例都关联有一个单一的 input 可读 流和单一的 output 可写 流。output 流用于打印提示符,以便用户输入到达并从 input 流中读取。
rl.question(query[, options])#
query<string>写入output的语句或查询,位于提示符之前。options<Object>signal<AbortSignal>可选地允许使用AbortSignal取消question()。
- 返回:
<Promise>一个以用户响应query的输入完成的 Promise。
rl.question() 方法通过将 query 写入 output 来显示它,等待在 input 上提供用户输入,然后调用 callback 函数并将提供的输入作为第一个参数传递。
调用时,如果 input 流已暂停,rl.question() 将会恢复它。
如果创建 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}`);
类:readlinePromises.Readline#
new readlinePromises.Readline(stream[, options])#
stream<stream.Writable>一个 TTY 流。options<Object>autoCommit<boolean>如果为true,则无需调用rl.commit()。
rl.clearLine(dir)#
dir<integer>-1:从光标向左清除1:从光标向右清除0:清除整行
- 返回:此实例
rl.clearLine() 方法向待处理动作的内部列表中添加一个动作,该动作按 dir 指定的方向清除相关 stream 的当前行。除非构造函数中传入了 autoCommit: true,否则请调用 rl.commit() 以查看此方法的效果。
rl.clearScreenDown()#
- 返回:此实例
rl.clearScreenDown() 方法向待处理动作的内部列表中添加一个动作,该动作从光标当前位置向下清除相关流。除非构造函数中传入了 autoCommit: true,否则请调用 rl.commit() 以查看此方法的效果。
rl.commit()#
- 返回:
<Promise>
rl.commit() 方法将所有待处理动作发送到相关的 stream 并清除待处理动作的内部列表。
rl.cursorTo(x[, y])#
rl.cursorTo() 方法向待处理动作的内部列表中添加一个动作,该动作将光标移动到相关 stream 中的指定位置。除非构造函数中传入了 autoCommit: true,否则请调用 rl.commit() 以查看此方法的效果。
rl.moveCursor(dx, dy)#
rl.moveCursor() 方法向待处理动作的内部列表中添加一个动作,该动作相对于其当前位置在相关 stream 中移动光标。除非构造函数中传入了 autoCommit: true,否则请调用 rl.commit() 以查看此方法的效果。
rl.rollback()#
- 返回:此实例
rl.rollback 方法清除内部待处理动作列表,而不将其发送到相关的 stream。
readlinePromises.createInterface(options)#
options<Object>input<stream.Readable>要监听的 可读 流。此选项是必需的。output<stream.Writable>要写入 readline 数据的 可写 流。completer<Function>用于 Tab 自动补全的可选函数。terminal<boolean>如果input和output流应被视为 TTY,并且应向其写入 ANSI/VT100 转义码,则设为true。默认值:实例化时检查output流上的isTTY。history<string[]>历史记录行的初始列表。仅当用户设置terminal为true或由内部output检查触发时,此选项才有意义,否则根本不会初始化历史记录缓存机制。默认值:[]。historySize<number>保留的历史记录行的最大数量。要禁用历史记录,请将此值设为0。仅当用户设置terminal为true或由内部output检查触发时,此选项才有意义,否则根本不会初始化历史记录缓存机制。默认值: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。signal<AbortSignal>允许使用 AbortSignal 关闭接口。
- 返回:
<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}`);
});
如果此实例的 terminal 为 true,则如果 output 流定义了 output.columns 属性,并且在列数发生变化时触发 'resize' 事件,那么该 output 流将获得最佳兼容性(当 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 可读 流和单一的 output 可写 流。output 流用于打印提示符,以便用户输入到达并从 input 流中读取。
rl.question(query[, options], callback)#
query<string>写入output的语句或查询,位于提示符之前。options<Object>signal<AbortSignal>可选地允许使用AbortController取消question()。
callback<Function>一个回调函数,当用户响应query时,该函数将被调用并传入用户的输入。
rl.question() 方法通过将 query 写入 output 来显示它,等待在 input 上提供用户输入,然后调用 callback 函数并将提供的输入作为第一个参数传递。
调用时,如果 input 流已暂停,rl.question() 将会恢复它。
如果创建 readline.Interface 时将 output 设置为 null 或 undefined,则不会写入 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>要监听的 可读 流。此选项是必需的。output<stream.Writable>要写入 readline 数据的 可写 流。completer<Function>用于 Tab 自动补全的可选函数。terminal<boolean>如果input和output流应被视为 TTY,并且应向其写入 ANSI/VT100 转义码,则设为true。默认值:实例化时检查output流上的isTTY。history<string[]>历史记录行的初始列表。仅当用户设置terminal为true或由内部output检查触发时,此选项才有意义,否则根本不会初始化历史记录缓存机制。默认值:[]。historySize<number>保留的历史记录行的最大数量。要禁用历史记录,请将此值设为0。仅当用户设置terminal为true或由内部output检查触发时,此选项才有意义,否则根本不会初始化历史记录缓存机制。默认值: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 属性,并且在列数发生变化时触发 'resize' 事件,那么该 output 流将获得最佳兼容性(当 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])#
stream<stream.Readable>interface<readline.InterfaceConstructor>
readline.emitKeypressEvents() 方法使给定的 可读 流开始触发对应于所接收输入的 'keypress' 事件。
可选地,interface 指定一个 readline.Interface 实例,当检测到复制粘贴的输入时,在该实例上禁用自动补全。
如果 stream 是一个 TTY,则它必须处于原始模式 (raw mode)。
任何 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+U 或 Ctrl+K 删除的文本 |
| Meta+Y | 循环查看之前删除的文本 | 仅当最后一次按键是 Ctrl+Y 或 Meta+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+W 或 Ctrl +Backspace | 向后删除到单词边界 | Ctrl+Backspace 在 Linux、Mac 和 Windows 上无效 |
| Ctrl+Delete | 向前删除到单词边界 | 在 Mac 上无效 |
| Ctrl+左箭头 或 Meta+B | 单词左移 | Ctrl+左箭头 在 Mac 上无效 |
| Ctrl+右箭头 或 Meta+F | 单词右移 | Ctrl+右箭头 在 Mac 上无效 |
| Meta+D 或 Meta +Delete | 向右删除单词 | Meta+Delete 在 Windows 上无效 |
| Meta+Backspace | 向左删除单词 | 在 Mac 上无效 |