在 spawn 的子进程中保持命令行颜色

2023-11-20

本文首发于我的博客,转载请注明出处:https://kohpoll.github.io/blo...

最近在用子进程运行 gulpfile.js 的时候发现终端上的颜色全部没有了,很是奇怪。经过一些研究,最终解决了问题,同时也总结了一些相关知识。希望对你有帮助。

终端颜色

相信大家都知道,我们平常使用的 terminal 是可以输出各种颜色的,并不仅仅只是充满 geek 味道的黑绿。这些颜色其实是通过一种叫做 ANSI/VT100 控制序列来标记的,这些字符本身不可见,会被 shell 解析后使用合适的颜色来渲染。

比如,命令 echo -e "\033[31m red \033[39m" 就可以在终端打印红色的文字。

其中的 \033[31m\033[39m 就是特殊的控制序列,\033[31m 表示红色的前景(文字)色,\033[39m 表示默认的前景(文字)色。而选项 -e 则是让 echo 启用反斜杠控制字符的转换(默认是不转换的)。

实际上,我们还可以使用 echo -e "\x1b[31m red \x1b[39m" 来输出红色文字。\x1b 是十六进制表示,刚好等于八进制表示的 033

除了前景色,还可以通过序列来表示背景色。更多的序列,可以参考:http://misc.flogisoft.com/bas...

那在 Node 中我们该如何打印带颜色的内容呢?其实很简单,我们只要使用一样的控制序列就可以了。

比如下面的代码,都可以打印红色文字。

console.log('\u001b[31m red \u001b[39m');
console.log('\033[31m red \033[39m'); // 这行代码在 strict 模式下将会报错
console.log('\x1b[31m red \x1b[39m');

但是这么多的控制序列很难记住,写起来很麻烦,给别人看更是如天书一般难懂。我推荐直接使用 chalk 这样的 npm 包,代码瞬间简洁、清晰了许多:

const chalk = require('chalk');
console.log(chalk.red('red'));

chalk 实际上直接使用了 ansi-styles 这个包,从其源码中可以看到,原理就是对字符串增加特殊的控制序列:https://github.com/chalk/ansi...

spawn

我在之前的文章中介绍过 Node 子进程的用法,感兴趣的可以进行阅读。

我比较喜欢使用 spawn 的方式,因为它可以通过 stream 的方式操作子进程的输出,非常方便。比如下面的代码:

const log = [];
const path = require('path');
const child = require('child_process').spawn(
  'node',
  [
    require.resolve('gulp/bin/gulp'),
    '--gulpfile', path.join(__dirname, 'gulpfile.js'),
    '--cwd', process.cwd()  
  ],
  {
    'stdio': 'pipe',
    'cwd': process.cwd()
  }
);
child.stdout.pipe(process.stdout);
child.stdout.on('data', (data) => {
  // collect the data
  log.push(data.toString('utf8'))
});

这里指定 stdio: 'pipe' 后,我们可以操作子进程的 stdout,比如将它的输出收集起来然后写入数据库做记录之类的。

如果尝试运行上面的脚本会发现,之前直接通过 gulp 执行 gulpfile.js 时漂亮的颜色消失了,全部变成默认颜色了。

process.stdout.isTTY

实际上,在 Node 中执行的进程我们可以通过 process.stdout.isTTY 这个属性来判断它是否在终端(terminal)终端环境中执行。

通过 spawn 并配置了 stdio: 'pipe' 开启的子进程,process.stdout.isTTY 属性会是 undefined

比如下面的代码:

// parent.js
const child = require('child_process').spawn(
  'node', ['./child.js'],
  {'stdio': 'pipe'}
);
child.stdout.pipe(process.stdout);

// child.js
console.log('process.stdout.isTTY=', process.stdout.isTTY);

在终端执行 node parent.js 会输出 process.stdout.isTTY= undefined

单独执行 node child.js 会输出 process.stdout.isTTY= true

gulp 背后的颜色功能,实际上使用的是 chalk

chalk 会使用 supports-color 做是否支持终端颜色的判断。从其源码中我们看到,它正是通过 process.stdout.isTTY 来进行判断的,如果该属性不为 true,则认为不支持:https://github.com/chalk/supp...

因此,由于被认为是在不支持终端颜色的环境中执行,我们之前代码中的所有输出就都不再带有颜色了。

另外我们还看到,supports-color 还会检查命令行参数中是否有 --color 选项,如果有的话就直接启用,不再检查 proess.stdout.isTTY。这正是我要的解决方案,于是,将代码修改成如下后,问题得到解决:

const log = [];
const path = require('path');
const child = require('child_process').spawn(
  'node',
  [
    require.resolve('gulp/bin/gulp'),
    '--gulpfile', path.join(__dirname, 'gulpfile.js'),
    '--cwd', process.cwd(),
    '--color' // preserve the terminal color  
  ],
  {
    'stdio': 'pipe',
    'cwd': process.cwd()
  }
);
child.stdout.pipe(process.stdout);
child.stdout.on('data', (data) => {
  // collect the data
  log.push(data.toString('utf8'))
});

stdio: inherit

实际上,我们也可以指定 stdio: 'inherit' 来让子进程直接使用父进程的 IO。这时子进程的 process.stdout.isTTY 会是 true。这是因为直接使用了父进程的 IO,所以它实际上还是在终端环境中执行的。

但是,这个方法会导致子进程不再拥有 stdout 属性,无法通过代码获取到子进程的任何标准输出,所以我最终并没有采用。

参考资料

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

在 spawn 的子进程中保持命令行颜色 的相关文章

随机推荐

  • 用SRGAN提升图片清晰度(TensorFlow)

    近两年GAN Generative Adversarial Network 相关的论文大火了一把 我自己也体验了几次 确实是很神奇的网络 GAN的各种变体基本都是用来生成图片的 关于GAN相关的说明这里不多讲 如有需要了解的可以自行搜索 但
  • 量化交易之linux篇 - shell脚本 - 遍历当前目录并判断文件类型及后缀

    bin bash 第一种写法 for file in ls do if f file file py then echo file is py file elif f file file txt then echo file is txt
  • python教程05-算术运算符的基本使用、赋值运算符的使用、比较运算符的使用、逻辑运算符的使用

    python教程 小白入门 2020 7 18 学习目标 文章目录 python教程 小白入门 2020 7 18 P31 算术运算符的基本使用 P32 字符串里的算术运算符 P33 赋值运算符的使用 P34 赋值运算符的特殊场景 P35
  • 通过python程序调取摄像头画面

    目录 1 方法 2 演示代码 3 总结 1 方法 想要调取摄像头画面有这么几个前提 需要使用网络摄像头并且支持RTSP协议 摄像头要和你的电脑在同一个局域网下 需要知道摄像头的账号 密码 IP地址 RTSP协议是一种网络传输协议 不同的摄像
  • Java8中将list转换为用逗号隔开的字符串的几种方法

    1 使用谷歌的Joiner转换 引用guava包 1 public static
  • Go语言包管理(一)

    Go语言中的包 我们在使用其他语言 比如Java Python 都有类似包的概念 Go也不例外 其核心思想即为分组和模块化 人的大脑对庞大和复杂的事情很难掌控 可以对其采用分而治之的策略 使其模块化 从而更容易管理 如下是标准库中net包的
  • C++中的头文件和源文件的学习

    文章来源 博客园http www cnblogs com lidabo archive 2012 04 17 2454568 html 一 C 编译模式重点内容 通常 在一个C 程序中 只包含两类文件 cpp文件和 h文件 其中 cpp文件
  • Nvidia Apex安装与简单使用

    降低训练精度提高batch大小 512 512的图片在8G的显存上batch只能为1 2 使用了apex后可以增大到10 只支持pytorch 官网地址 NVIDIA apex 官方说支持Ubuntu系统 Windows只是实验性的 但是我
  • 这些mysql基础命令、基础知识还记得吗?(面试,学习,复习都可以)一万三千字总结

    mysql知识清单 详细 互联网通信 一 什么是互联网通信 二 互联网通信角色 三 通信模型 MySql关系型数据库服务器 一 介绍 二 名词解释 三 数据库服务器分类 四 卸载mysql服务器 五 基本信息 六 MySql服务器配置 七
  • Linux 桌面应用程序

    调研 要在Linux下开发具有图形界面的桌面应用程序 我们有许多选择 当然 除了语言方面的考虑外 我们更多的是考虑窗口部件工具箱 以前称为控件库或组件库 的选择 这将在很大程度上影响我们的开发效率 如果考虑到跨平台应用 选择Java的Swi
  • php array_diff 二位数组,PHP比较二维数组,求大神赐教

    一个参与活动名单的二维数组 arr1 array array name gt 张三 phone gt 1354459845 address gt 一街5号 array name gt 李四 phone gt 1323439845 addre
  • eclipse导入项目,项目名出现红叉的情况

    今天用eclipse导入同事发给我的一个项目之后 项目名称上面出现红叉 但是其他地方都没有红叉 仅仅是在项目名称上面有红叉 于是上网查了查资料记下来 1 导入项目之前 请确认工作空间编码已设置为utf 8 window gt Prefere
  • 想写一个安全服务工程师的培训教材或者手册

    最近有个朋友搞了个公司 打算找人接安服项目 让我找点安全培训的教材 看了一大圈的书籍目录感觉都比较片面 或者专注一个方面 对于一些几乎0基础的应届生或实习生来说不太能够在几个月时间内上手项目 csdn 知乎和百度上其他类似网站的0基础培训思
  • 【Shell牛客刷题系列】SHELL2 打印文件的最后5行:优雅的解决方案~

    该系列是基于牛客Shell题库 针对具体题目进行查漏补缺 学习相应的命令 刷题链接 牛客题霸 Shell篇 该系列文章都放到专栏下 专栏链接为 专栏 Linux 欢迎关注专栏 本文知识预告 首先学习用于查看文件尾部内容的tail命令 然后给
  • iPhone: There is no SDK with the name or path iphoneos XXX

    for ever 2010 10 25 环境 MAC OS 10 6 4 老的iPhone 项目 使用最新的 XCode 3 2进行编译 报错 iPhone There is no SDK with the name or path iph
  • vue中组件的划分(重点)

    vue中组件的划分 重点 组件的职能划分 如果要将 Vue 组件按照职能划分 我们可以将其分为两种类型 容器组件和展示组件 容器组件和展示组件的概念来自于 Redux 文档 那么首先什么是容器组件呢 顾名思义 它是一个容器性质的组件 我们可
  • 如何在ubuntu安装powershell

    在Ubuntu上安装PowerShell可以通过以下步骤来完成 1 安装依赖软件 可以使用以下命令安装所需的依赖软件 sudo apt get install curl libunwind8 gettext apt transport ht
  • 使用postman怎么都访问不到项目,也不报错

    使用postman怎么都访问不到项目 也不报错 可能问题1 本地路径没有对应你访问的访问路径 在你的C盘下面的C Windows System32 drivers etc这个路径下找到host文件 你127 0 0 1 对应的是什么路径 你
  • 设计模式(不懂)

    面试中经常问到设计模式 我才对这个东西了解了一下 才发现他是没有开发的新大陆 是oo设计的更高级别 能把设计模式搞懂 那oo你就搞的差不多了 随便看了还是很有意思的 虽然不怎么懂 百科名片 相关书籍 设计模式 Design pattern
  • 在 spawn 的子进程中保持命令行颜色

    本文首发于我的博客 转载请注明出处 https kohpoll github io blo 最近在用子进程运行 gulpfile js 的时候发现终端上的颜色全部没有了 很是奇怪 经过一些研究 最终解决了问题 同时也总结了一些相关知识 希望