Node.js 交互式命令行解析器 REPL

 2016年11月12日    331     声明


repl是Node.js提供的一个Read-Eval-Print-Loop (REPL,读取-执行-输出-循环)实现,它即可以做为一个独立的程序使用,又可以包含在其它应用中使用。REPL是一个互式命令行解析器,它提供了一个交互式的编程环境,它可以实时的验证你所编写的代码,非常适合于验证Node.js和JavaScript的相关API。

  1. REPL模块介绍
  2. 类:REPLServer
  3. repl.start([options]) - 创建实例

1. REPL模块介绍

repl模块中,导出了一个repl.REPLServer。运行时,repl.REPLServer实例会接受用户输入的各个行,并根据用户定义的评 估函数进行解析,然后输出结果。输入和输出可以是stdinstdout,也可以是Node.js中的任何

repl.REPLServer实例支持输入自动完成,简化的Emacs风格,多行输入,ANSI风格的输出,保存和恢复当前REPL会话的状态,错误恢复以及可定制的评价功能。

1.1 命令与特殊的键

以下几个特殊的指令可以在所有REPL实例中使用:

  • .break - 当处理多行表达式输入时,输入.break命令(或输入<ctrl>-C)将中止之后的输入或表达式处理。
  • .clear - 重置 REPL的 context 为一个空对象,并清空当前任何多行输入表达式。
  • .exit - 关闭 I/O 流,会造成 REPL 退出
  • .help - 显示命令列表
  • .save - 将当前 REPL 会话保存到一个文件: > .save ./file/to/save.js
  • .load - 加载一个文件到当前 REPL 会话: > .load ./file/to/load.js
  • .editor - 进入编辑模式 (<ctrl>-D 完成编辑,<ctrl>-C 取水编辑)
> .editor
// Entering editor mode (^D to finish, ^C to cancel)
function welcome(name) {
  return `Hello ${name}!`;
}

welcome('Node.js User');

// ^D
'Hello Node.js User!'
>

在REPL中,可以使用以下特殊键:

  • <ctrl>-C - 按下一次时,结果与.break命令相同。在一个空白行中按下两次时,结果与.exit命令相同
  • <ctrl>-D - 与.exit命令相同
  • <tab> - 在空白行中按下时,列出全局与本地变量。在进行输入时按下时,显示相关自动完成选项。


1.2 默认解析

默认情况下,所有repl.REPLServer实例会使用一个默认解析函数,对JavaScript表达式或Node.js内置模块进行解析。但这种默认行为,可以在创建repl.REPLServer时指定一个解析函数进行替换。


JavaScript表达式

默认编译器支持对JavaScript表达式的直接解析:

> 1 + 1
2
> var m = 2
undefined
> m + 1
3

除非是使用了语句块(如:{…})或函数,或在全局作用域中声明的变量或使用该无功关键字关键字声明的变量。


全局与本地作用域

默认情况下,可以在全局范围内访问任何存在的变量。例如:

const repl = require('repl');
var msg = 'message';

repl.start('> ').context.m = msg;

context对象的属性可以在REPL本地出现:

$ node repl_test.js
> m
'message'


访问Node.js核心模块

默认情况下,解析器会在使用时自动加载Node.js核心模块。除非另外声明全局或局部变量,输入fs时会解析global.fs = require('fs')

> fs.createReadStream('./some/file');


赋值下划线(_)变量

默认解析器,在默认情况下会通过一个特殊的变量_来指定解析表达式:

> [ 'a', 'b', 'c' ]
[ 'a', 'b', 'c' ]
> _.length
3
> _ += 1
4

当显式的为_设置值时,会改变这种默认方式。


1.3 自定义解析函数

创建一个新的repl.REPLServer实例后,可能会传入一个自定义的解析函数。

以下是一个用于语言转换的说明示例:

const repl = require('repl');
const Translator = require('translator').Translator;

const myTranslator = new Translator('en', 'fr');

function myEval(cmd, context, filename, callback) {
  callback(null, myTranslator.translate(cmd));
}

repl.start({prompt: '> ', eval: myEval});


1.4 自定义REPL输出

默认情况下,repl.REPLServer实例会在向所提示的可写流(默为process.stdout)写入输出前,使用util.inspect()方法进行格式化。可以向writer参数传入一个函数对repl.REPLServer实例进行完全自定义输出:

const repl = require('repl');

const r = repl.start({prompt: '>', eval: myEval, writer: myWriter});

function myEval(cmd, context, filename, callback) {
  callback(null,cmd);
}

function myWriter(output) {
  return output.toUpperCase();
}


2. 类:REPLServer

添加于v0.1.91

repl.REPLServer类继承自readline.Interface类。repl.REPLServer实例通过repl.start()方法创建,且不能显示的使用new关键字创建。

2.1 repl.REPLServer对象事件

Event: 'exit'

添加于v0.7.7

'exit'事件会在REPL退出或是输入.exit命令后发送,用户按两次<ctrl>-C发送SIGINT信号,或是按<ctrl>-D向输入流发送'end'信号。被调用的监听函数没有任何参数:

replServer.on('exit', () => {
  console.log('Received "exit" event from repl!');
  process.exit();
});


Event: 'reset'

添加于v0.11.0

'reset'事件会在REPL的内容被重置时发送。无论是否收到.clear命令都会发生,除非REPL使用默认的解析器且repl.REPLServer创建实例时将useGlobal选项设置为true。其回调函数中仅有一个context参数:

const repl = require('repl');

function initializeContext(context) {
  context.m = 'test';
}

var r = repl.start({prompt: '>'});
initializeContext(r.context);

r.on('reset', initializeContext);

以上代码执行时,全局的'm'变量可以被修改但使用.clear命令时会被重置为初始值:

$ ./node example.js
>m
'test'
>m = 1
1
>m
1
>.clear
Clearing context...
>m
'test'
>


2.2 repl.REPLServer对象方法

replServer.defineCommand(keyword, cmd) - 定义命令

添加于v0.3.0

  • keyword <String> 命令关键字 (.前缀)
  • cmd <Object> | <Function> 被命令调用的处理函数
  • replServer.displayPrompt()方法用于向REPL添加新的以.为前缀的命令。这样输入命令时,首先输入.其次是关键字。cmd可以是一个函数,或是一个包含以下参数的对象:

  • help <String> 被 .help 命令所显示的帮助信息 (可选)。
  • action <Function> 要执行的函数

如,向REPL实例添加两个新命令:

const repl = require('repl');

var replServer = repl.start({prompt: '> '});
replServer.defineCommand('sayhello', {
  help: 'Say hello',
  action: function(name) {
    this.lineParser.reset();
    this.bufferedCommand = '';
    console.log(`Hello, ${name}!`);
    this.displayPrompt();
  }
});
replServer.defineCommand('saybye', function() {
  console.log('Goodbye!');
  this.close();
});

定义后可以在命令行中像下面这样调用:

> .sayhello Node.js User
Hello, Node.js User!
> .saybye
Goodbye!


replServer.displayPrompt([preserveCursor]) - 显示提示

添加于v0.1.91

  • preserveCursor <Boolean>

replServer.displayPrompt()方法会为REPL实例准备用户输入,并在一个新行打印提示信息。

preserveCursortrue时,光标不会放在0的位置。


3. repl.start([options]) - 创建实例

repl.start()是一个用于使用REPL实例的工厂方法。options参数是一个可包含以下参数的对象:

  • prompt <String> 要显示的输入提示。默认为 >
  • input <Readable> REPL要读取的输入流。默认为process.stdin
  • output <Writable> REPL要写入的输出流。默认为process.stdout
  • terminal <boolean> 如果为 true,指定的output 需要做为一个TTY终端处理。
  • eval <Function> 对每个输入行进行解析时所执行的函数。默认为 eval()
  • useColors <boolean> 如果为 true,指定的 writer 函数需要包括 ANSI 颜色风格替代输出。如果自定义 writer 函数,这会没有效果。默认为 REPL 实例的 terminal
  • useGlobal <boolean> 如果为 true,指定的默认解析函数会使用JavaScriptglobal上下文为实例创建新的独立内容。默认为false
  • ignoreUndefined <boolean> 如果为 true,指定的默认写入,在评价为undefined时将不会返回一个值。默认为false.
  • writer <Function> 在向output写入数据时,调用的格式化函数。默认为util.inspect()
  • completer <Function> 用于自定义Tab自动完成的可选函数。参见 readline.InterfaceCompleter
  • replMode - 指定所有的 JavaScript 命令的运行模式,可以是严格模式、默认模式、或hybrid模式(code模式)。可选值有:
    • repl.REPL_MODE_SLOPPY - 在松散模式下执行
    • repl.REPL_MODE_STRICT - 在严格模式下执行
    • repl.REPL_MODE_MAGIC - 在默认模式下执行
  • breakEvalOnSigint - 当收到 SIGINT 信号后,停止当前解析。如,按下 Ctrl+C 时。这不能用于自定义的eval 函数。默认为false