NodeJs-TTY

class TTY

TTY 模块是 Node.js 提供的一个与终端(终端设备,Terminal)交互的模块。TTY 是 Teletypewriter 的缩写,最初是指一种远程交互系统。如今,tty 模块允许我们访问和处理命令行输入、输出流,通常与 process.stdinprocess.stdout 一起使用,处理交互式命令行应用中的输入输出行为。


1. 引入 tty 模块

在使用 tty 模块之前,需要将其引入到项目中:

const tty = require('tty');

2. 基本概念

  • 终端(TTY):终端设备或终端仿真器(例如,shell、命令提示符),提供文本输入输出功能。
  • 标准输入(stdin)标准输出(stdout):分别表示输入设备(通常是键盘)和输出设备(通常是屏幕)。
  • tty:Node.js 中的 process.stdinprocess.stdout 是流对象,它们也可以是 TTY 对象。tty 模块可以检测这些流是否连接到 TTY。

3. 检测 TTY

可以使用 process.stdin.isTTYprocess.stdout.isTTY 来检查当前进程的标准输入和输出是否连接到 TTY 设备。

示例:

if (process.stdout.isTTY) {
  console.log('This is running in a terminal');
} else {
  console.log('Not running in a terminal');
}

4. tty.isatty(fd) 方法

tty.isatty(fd) 用于检查给定的文件描述符(fd)是否关联到 TTY 设备。

参数:

  • fd:文件描述符,通常是 process.stdin.fdprocess.stdout.fd

返回值:

  • 返回 truefalse,表示文件描述符是否指向 TTY。

示例:

const tty = require('tty');

console.log(tty.isatty(process.stdin.fd)); // 输出: true (如果连接到终端)
console.log(tty.isatty(1));                // 输出: true (stdout)

5. Class: tty.ReadStream

tty.ReadStream 是继承自 net.Socket 的类,表示一个读取 TTY 输入的流。process.stdintty.ReadStream 类的实例,默认情况下它是标准输入的流。

属性:

  • readStream.isRaw:布尔值,指示是否启用了原始模式。
  • readStream.setRawMode(mode):将 readStream 切换到原始模式或关闭原始模式。

原始模式:

  • 在原始模式下,输入不会被解释为特殊字符(例如 Ctrl+C 退出)。
  • 启用原始模式适用于需要逐字节读取输入(如键盘输入)时。

示例:

const stdin = process.stdin;

stdin.setRawMode(true); // 启用原始模式
stdin.resume();         // 启用数据流
stdin.setEncoding('utf8');

stdin.on('data', (key) => {
  if (key === '\u0003') { // Ctrl+C 退出
    process.exit();
  }
  console.log(`You pressed: ${key}`);
});

在这个例子中,原始模式下每个按键的输入都会被读取并立即处理,而不会等待用户按下 Enter


6. Class: tty.WriteStream

tty.WriteStream 是继承自 net.Socket 的类,表示一个输出到 TTY 的流。process.stdoutprocess.stderr 都是 tty.WriteStream 类的实例。

属性:

  • writeStream.columns:当前终端的列数(宽度)。如果流没有连接到 TTY,返回 undefined
  • writeStream.rows:当前终端的行数(高度)。如果流没有连接到 TTY,返回 undefined
  • writeStream.isTTY:指示流是否连接到 TTY 设备。

动态调整终端大小:

当终端大小发生变化时,resize 事件会触发。

示例:

if (process.stdout.isTTY) {
  console.log(`Terminal size: ${process.stdout.columns}x${process.stdout.rows}`);
}

process.stdout.on('resize', () => {
  console.log(`Resized to: ${process.stdout.columns}x${process.stdout.rows}`);
});

在这个例子中,resize 事件会在终端大小发生变化时触发,输出新的终端宽度和高度。


7. tty 模块的事件和交互

tty.ReadStreamtty.WriteStream 类都继承自 net.Socket,因此支持标准的 streamsocket 事件,如 dataenderrorclose

  • data 事件:当输入流收到数据时触发。
  • end 事件:当输入流结束时触发。
  • resize 事件:当终端大小发生变化时触发(仅适用于 tty.WriteStream)。

示例:监听输入的 data 事件

const stdin = process.stdin;

stdin.setRawMode(true);   // 启用原始模式
stdin.resume();           // 启用数据流
stdin.setEncoding('utf8');

stdin.on('data', (key) => {
  if (key === '\u0003') { // Ctrl+C 退出
    process.exit();
  }
  console.log(`You pressed: ${key}`);
});

在此例中,我们监听 data 事件,捕获用户输入并输出。


8. 终端控制字符

终端的控制字符(例如,Ctrl+C)通常会发送特定的字符代码。你可以通过启用原始模式来拦截这些字符,并自定义它们的行为。

  • Ctrl+C (\u0003):通常用于终止进程。
  • Ctrl+D (\u0004):发送 EOF(End of File)信号,表示输入结束。
  • Ctrl+Z (\u001A):通常用于将进程挂起。

通过 setRawMode(true),可以捕获并处理这些控制字符,而不是让它们执行默认的行为。


9. Node.js 中的 tty 用途

tty 模块常用于构建命令行工具和交互式终端应用。使用 tty 模块,你可以:

  • 检查进程是否在终端中运行。
  • 处理和管理终端大小变化。
  • 读取用户输入并处理特殊字符。
  • 创建复杂的命令行交互(如进度条、菜单等)。

示例:构建一个简单的交互式命令行应用

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

rl.question('What is your name? ', (answer) => {
  console.log(`Hello, ${answer}!`);
  rl.close();
});

在这个例子中,我们使用 readline 模块来读取用户输入,但底层依赖的是 tty 流。


总结

TTY 模块提供了处理命令行输入输出的底层机制。通过 tty 模块,你可以检测进程是否在终端中运行,管理终端大小变化,启用原始模式捕获和处理输入,以及构建复杂的交互式命令行应用。结合 Node.js 的流和事件模型,tty 模块是构建强大 CLI 应用程序的关键工具。

评论区
评论列表
menu