【问题标题】:How can I pipe from terminal in Perl without losing color?如何在 Perl 中从终端进行管道传输而不会丢失颜色?
【发布时间】:2011-07-01 16:18:41
【问题描述】:

我正在尝试编写一个 perl 脚本,它获取 colorgcc 的输出(或任何其他将彩色文本打印到终端的脚本),添加/删除部分字符串,然后以 相同的方式打印结果 颜色作为输入字符串。

以下代码将在 color_produce_script 生成的每一行前面打印“Hello World”。输出将是全黑的,而输入是多色的。如何修改此脚本以保留原始颜色?

open(CMD, "color_producing_script |");

while(<CMD>) {
    print 'Hello World' . $_;
}

我正在使用 bash 终端。

编辑

根据出色的第一条评论,这本身不是 Perl 问题。只需运行color_producing_script | cat 就会去掉颜色。所以这个问题可以改写为“你如何强制颜色通过管道?”

编辑 2

看起来最新版本的gcc(1.3.2)在if子句中包含了CGCC_FORCE_COLOR环境变量,如果定义了,colorgcc强制颜色:

export CGCC_FORCE_COLOR=true

【问题讨论】:

  • 它不会剥离颜色;脚本在通过管道时不会产生颜色。

标签: perl bash colors terminal pipe


【解决方案1】:

color_producing_script 在管道中使用时会改变其行为吗?试试

color_producing_script | cat

在命令行。它可能有一个选项来强制颜色输出,即使它是。

Perl 脚本 colorgcc 专门检查是否输出到非 tty,如果是则跳过着色。

# Get the terminal type. 
$terminal = $ENV{"TERM"} || "dumb";

# If it's in the list of terminal types not to color, or if
# we're writing to something that's not a tty, don't do color.
if (! -t STDOUT || $nocolor{$terminal})
{
   exec $compiler, @ARGV
      or die("Couldn't exec");
}

编辑:

您可以通过以下一种或多种方式修改脚本:

  • 注释掉测试并使其始终产生颜色输出
  • 添加功能以支持读取设置是否着色的环境变量
  • ~/.colorgccrc 配置文件中添加支持color-always 选项的功能
  • 添加功能以支持在将其余选项传递给编译器之前剥离的始终显示颜色的命令行选项

您也可以使用expect 脚本unbuffer 来创建这样的伪tty:

unbuffer gcc file.c | cat

(其中cat 是备用收件人)。

所有这些都基于在命令行中使用colorgcc。在 Perl 脚本中应该有类似的东西来做类似的事情。

【讨论】:

  • 它确实改变了它的行为。 color_producing_script | cat 生成全黑文本。是否有标准的 bash shell 方法通过管道“强制”颜色?
  • @LexFridman:不,每个实用程序、程序或脚本都有自己的选项。外壳与它无关。示例:ls --color=alwaysgrep --color=alwaysack --color。这是你写的剧本吗?它是使用这些实用程序之一还是其​​他一些实用程序?你能告诉我更多关于它的作用或它是如何做到的吗?
  • 大多数语言和操作系统都提供了一种方法来判断输出流是否正在写入终端设备。着色脚本可以选择在写入文件或管道时更改其行为。
  • 该脚本是围绕 gcc C++ 编译器的常用 Perl 包装脚本,称为 colorgcc。我可以修改该脚本,但我想避免这种情况,因为它是使用 Ubuntu 打包系统分发的。它没有 --color=always 标志。所以问题就变成了,如何在将一个 perl 脚本传送到另一个脚本时保留颜色?
  • @LexFridman:请查看我编辑的答案。除了使用例如expect 创建一个伪tty 之外,在shell 中没有任何方法。
【解决方案2】:

ColorGCC的源码对这个话题说的很清楚了!

#! /usr/bin/perl -w
# ...
# from: colorgcc-4.1.2/colorgcc-4.1.2
# downloaded from: http://www.console-colors.de/index.php?n=ConsColors.Downloads
#
# Note:
#
# colorgcc will only emit color codes if:
# 
#    (1) Its STDOUT is a tty and
#    (2) the value of $TERM is not listed in the "nocolor" option.
#
# If colorgcc colorizes the output, the compiler's STDERR will be
# combined with STDOUT. Otherwise, colorgcc just passes the output from
# the compiler through without modification.
# .....
# If it's in the list of terminal types not to color, or if
# we're writing to something that's not a tty, don't do color.
if (! -t STDOUT || $nocolor{$terminal})
{
   exec $compiler, @ARGV
      or die("Couldn't exec");
}

除了在 Perl 中使用 Pty 而不是管道(正如 cjm 已经指出的那样),您还可以欺骗应用程序,使其认为它的标准输入是交互式的,而不是命令行上的管道。

例如:

# Linux
script -c "[executable string]" /dev/null

# FreeBSD, Mac OS X
script -q /dev/null "[executable string]"

更多解决方案见:bash: force exec'd process to have unbuffered stdout

【讨论】:

  • 好答案。对了。您准确地确定了问题所在。不确定用于 colorgcc 的版本控制系统,但我认为最新版本是 1.3.2。至少这是原始开发人员提供的以及 Ubuntu 软件包存储库所提供的。正如我的第二次编辑所描述的,这个版本实际上对您提供的 if 语句有一个关键的补充。另外,我应该注意,您提出的script -c 建议在保留颜色方面对我不起作用。
【解决方案3】:

许多生成彩色输出的程序会检测它们是否正在写入 TTY,如果不是,则关闭颜色。这是因为当您只想捕获文本时,颜色代码很烦人,因此它们会尝试自动“做正确的事情”。

从这样的程序捕获颜色输出的最简单方法是告诉它写入颜色,即使它没有连接到 TTY。您必须阅读该程序的文档以了解它是否具有该选项。

另一种选择是使用Pty 而不是管道。在 Perl 中,您可以使用 IO::Pty::HalfDuplexIO::Pty::Easy 来执行此操作,它们都是低级模块 IO::Pty 的高级包装器。

【讨论】:

  • 所以,很多人都提出了这个解决方案,而且它似乎是一个很棒的解决方案。谢谢。但我不知道如何让它工作。您提到的 Pty 或包装器仍然相当复杂,不幸的是,文档很薄弱。
猜你喜欢
  • 2016-11-24
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2022-11-15
  • 1970-01-01
  • 2011-06-26
相关资源
最近更新 更多