Readline

通过 require('readline') 可以加载该模块。Readline 模块提供了逐行读取 stream (比如 process.stdin)的能力。

注意,一旦调用该模块,Node.js 程序将不会自动关闭,需要开发者显式关闭接口。下面的代码演示了如何退出程序:

  1. const readline = require('readline');
  2. const rl = readline.createInterface({
  3. input: process.stdin,
  4. output: process.stdout
  5. });
  6. rl.question('What do you think of Node.js? ', (answer) => {
  7. // TODO: Log the answer in a database
  8. console.log('Thank you for your valuable feedback:', answer);
  9. rl.close();
  10. });

Class: Interface

该类是一个包含输入输出 stream 的 readline 接口。

rl.close()

该方法用于关闭 Interface 实例,取消对 inputoutput stream 的控制。调用该方法会触发 close 事件。

rl.pause()

该方法用于暂停执行 readline 中的 input stream,该 stream 稍后可以回复状态继续执行。

注意,该方法不会立即暂停 stream 的操作。调用该方法后将会触发多个事件,包括 line

rl.prompt([preserveCursor])

该方法为用户配置好输入环境,并将当前的 setPrompt 置于新行,指定用户输入的位置。如果 preserveCursor 的值为 true,则不允许光标位置被重置为 0

如果 input stream 已经暂停,也可以使用 createInterface 重置。

如果调用 createInterface 时,output 的值为 nullundefined,那么就不会写入提示符。

rl.question(query, callback)

该方法向用户显示 query,并在用户响应后执行 callback

如果 input stream 已经暂停,也可以使用 createInterface 重置。

如果调用 createInterface 时,output 的值为 nullundefined,那么就不会写入提示符。

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

rl.resume()

该方法用于恢复 input stream 的状态。

rl.setPrompt(prompt)

该方法用于设置提示符,举例来说,当你在命令行中运行 Node.js 时,你会发现一个 > 符号,这就是 Node.js 的提示符。

rl.write(data[, key])

该方法用于将 data 写入 output stream,除非调用 createInterface 时,output 的值为 nullundefinedkey 是一个对象,包含了一系列的键名。只有终端是 TTY 时,该方法才可用。

如果 input stream 已经暂停,也可以该方法重置。

  1. rl.write('Delete me!');
  2. // Simulate ctrl+u to delete the line written previously
  3. rl.write(null, {ctrl: true, name: 'u'});

事件:’close’

  • function () {}

当调用 close() 时会触发该事件。

input stream 接收到 end 事件时也会触发该事件。该事件触发之后,即可将 Interface 的实例视为已结束,比如 input stream 收到 ^D 时,就会被认为是 EOT

input sream 接收到 ^C(SIGINT)时,如果没有 SIGINT 事件监听器,那么也会触发该事件。

事件:’line’

  • function (line) {}

input stream 接收到行结束符(\n\r、或 \r\n)时,就会触发该事件。通常来说,当用户敲下回车键或者 return 键时,就会触发该事件。这对监听用户的输入来说是一个非常有用的钩子。

  1. rl.on('line', (cmd) => {
  2. console.log(`You just typed: ${cmd}`);
  3. });

事件:’pause’

  • function () {}

input stream 暂停时触发该事件。此外,当 input stream 接收到 SIGCONT 事件时也会触发该事件。

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

事件:’resume’

  • function () {}

input stream 从暂停中回复过来时触发该事件。

  1. rl.on('resume', () => {
  2. console.log('Readline resumed.');
  3. });

事件:’SIGCONT’

  • function () {}

该事件在 Windows 上不会触发。

input stream 向后台传送 ^ZSIGTSTP)时就会触发该事件,然后继续执行 fs(1)。该事件只有程序切换到后台前 stream 没有暂停才会触发。

  1. rl.on('SIGCONT', () => {
  2. // `prompt` will automatically resume the stream
  3. rl.prompt();
  4. });

事件:’SIGINT’

  • function () {}

input stream 接收到 ^CSIGINT)时就会触发该事件。如果接收到 SIGINT 却没有 SIGINT 事件监听器,那么就会触发 pause 事件。

  1. rl.on('SIGINT', () => {
  2. rl.question('Are you sure you want to exit?', (answer) => {
  3. if (answer.match(/^y(es)?$/i)) rl.pause();
  4. });
  5. });

事件:’SIGTSTP’

  • function () {}

该事件在 Windows 上不会触发。

input stream 向后台传送 ^ZSIGTSTP)时就会触发该事件。如果接收到 SIGINT 却没有 SIGINT 事件监听器,那么就会将程序切换到后台。

当程序通过 fg 回复时,会触发 pauseSIGCONT 事件。开发者可以使用任意一个事件恢复 stream 的状态。

如果程序切换到后台前 stream 暂停了,那么就不会触发 pauseSIGCONT 事件。

  1. rl.on('SIGTSTP', () => {
  2. // This will override SIGTSTP and prevent the program from going to the
  3. // background.
  4. console.log('Caught SIGTSTP.');
  5. });

实例:Tiny CLI

下面代码演示了如何使用上述方法创建一个命令行接口:

  1. const readline = require('readline');
  2. const rl = readline.createInterface(process.stdin, process.stdout);
  3. rl.setPrompt('OHAI> ');
  4. rl.prompt();
  5. rl.on('line', (line) => {
  6. switch(line.trim()) {
  7. case 'hello':
  8. console.log('world!');
  9. break;
  10. default:
  11. console.log('Say what? I might have heard `' + line.trim() + '`');
  12. break;
  13. }
  14. rl.prompt();
  15. }).on('close', () => {
  16. console.log('Have a great day!');
  17. process.exit(0);
  18. });

实例:逐行读取文件 stream

一个常见的操作就是使用 readline 的 input 选项读取文件 stream。下面带那么演示了如何逐行解析文件:

  1. const readline = require('readline');
  2. const fs = require('fs');
  3. const rl = readline.createInterface({
  4. input: fs.createReadStream('sample.txt')
  5. });
  6. rl.on('line', (line) => {
  7. console.log('Line from file:', line);
  8. });

readline.clearLine(stream, dir)

该方法从指定的方法清除 TTY stream 中的当前行,其中 dir 有如下可选值:

  • -1,从光标向左
  • 1,从光标向右
  • 0,正行

readline.clearScreenDown(stream)

该方法用于清空屏幕上从光标位置开始的内容。

readline.createInterface(options)

该方法创建一个 readline Interface 实例。可选参数 options 包含以下属性:

  • input,监听的可读 stream,必选属性
  • output,输入 realine 数据的可写 stream,可选属性
  • completer,可选函数,用于自动补全
  • terminal,如果需要 inputoutput stream 的行为类似于 TTY 并使用 ANSI/VT1000 编码,则设置该属性为 true。默认在 output stream 实例化时执行 isTTY 检查
  • historySize,指定保留多少行的历史记录,默认值为 30

completer 函数接收用户的输入并返回一个数组,该数组包含两条记录:

  1. 复核补全的字符串数组
  2. 用于匹配的子串

最终结果类似于:[[substr1, substr2, ...], originalsubstring]

  1. function completer(line) {
  2. var completions = '.help .error .exit .quit .q'.split(' ')
  3. var hits = completions.filter((c) => { return c.indexOf(line) == 0 })
  4. // show all completions if none found
  5. return [hits.length ? hits : completions, line]
  6. }

如果给 completer 传入两个参数,那么也可以以异步的方式执行:

  1. function completer(linePartial, callback) {
  2. callback(null, [['123'], linePartial]);
  3. }

createInterface 常常和 process.stdinprocess.stdout 配合使用,用来接收用户的输入:

  1. const readline = require('readline');
  2. const rl = readline.createInterface({
  3. input: process.stdin,
  4. output: process.stdout
  5. });

一旦创建了 readline 的实例之后,开发者通常会监听 line 事件。

如果 readline 实例的 terminal 的值为 true 并且定义了 output.columns 属性,那么 output stream 就会保持最佳兼容性。当行大小发生变化时(如果是 TTY,则 process.stdout 会自动处理此类变化),会在 output stream 上触发 resize 事件。

readline.cursorTo(stream, x, y)

在给定的 TTY stream 中,该方法用于将光标移动到特定位置。

readline.moveCursor(stream, dx, dy)

在给定的 TTY 中,该方法相对于光标的当前位置移动光标。