4

I'm trying to write a perl script which takes the output of colorgcc (or any other script that prints colored text to terminal), adds/removes parts of the string, and then prints the result in the same color as the input string.

The following code will print "Hello World" in front of each line produced by the color_producing_script. The output will be all black, while the input is multicolored. How can I modified this script to conserve the original colors?

open(CMD, "color_producing_script |");

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

I'm using bash terminal.

Edit

Per the excellent first comment, this is not a Perl issue per se. Just running color_producing_script | cat strips the color off. So the question can be rephrased to "How do you force color through the pipe?"

Edit 2

It looks like the latest version of gcc (1.3.2) includes the CGCC_FORCE_COLOR environment variable in the if clause, and if it's defined, colorgcc forces color:

export CGCC_FORCE_COLOR=true
Alan Turing
  • 12,223
  • 16
  • 74
  • 116
  • It doesn't *strip* the color off; the script doesn't produce color when going through a pipe. – Gabe Feb 22 '11 at 15:13

3 Answers3

4

Does color_producing_script change its behavior when it's used in a pipe? Try

color_producing_script | cat

at the command line. It may have an option to force color output even when it is.

The Perl script, colorgcc, is specifically checking to see if output is to a non-tty and skipping the colorization if that's the case.

# 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");
}

Edit:

You could modify the script in one or more of the following ways:

  • comment out the test and make it always produce color output
  • add functionality to support reading an environment variable that sets whether to colorize
  • add functionality to support a color-always option in the ~/.colorgccrc configuration file
  • add functionality to support a color-always command line option that you strip before passing the rest of the options to the compiler

You could also use the expect script unbuffer to create a pseudo-tty like this:

unbuffer gcc file.c | cat

(where cat is a standin recipient).

All of this is based on using colorgcc from the command line. There should be analogs for doing similar things within a Perl script.

Dennis Williamson
  • 346,391
  • 90
  • 374
  • 439
  • 1
    It does change its behavior. `color_producing_script | cat` produces all black text. Is there a standard bash shell method for "forcing" color through the pipe? – Alan Turing Feb 22 '11 at 01:42
  • 1
    @LexFridman: No, each utility, program or script has its own option. The shell has nothing to do with it. Examples: `ls --color=always`, `grep --color=always`, `ack --color`. Is this a script you wrote? Does it use one of these utilities or some other? Can you tell me more about what it does or how it does it? – Dennis Williamson Feb 22 '11 at 02:42
  • Most languages and operating systems provide for a way to tell whether an output stream is writing to a terminal device. Colorizing scripts have the option of changing their behavior when writing to, say, a file or a pipe. – mob Feb 22 '11 at 04:16
  • The script is a commonly available Perl wrapper script around the gcc C++ compiler, called [colorgcc](http://schlueters.de/colorgcc.html). I could modify that script, but I would like to avoid that since it is distributed in my case using the Ubuntu packaging system. There is no --color=always flag for it. So the question becomes, how do I preserve color when piping one perl script to another? – Alan Turing Feb 22 '11 at 07:56
  • @LexFridman: Please see my edited answer. There's not any way in the shell, other than to create a pseudo-tty using, for example, `expect`. – Dennis Williamson Feb 22 '11 at 15:03
  • @Dennis, thank you! Very good suggestions. The unbuffer one didn't work but it looks like the latest version of gcc (1.3.2) includes the CGCC_FORCE_COLOR environment variable in the if clause, and if it's defined, colorgcc forces color. – Alan Turing Feb 23 '11 at 06:01
  • @LexFridman: Where do you see that? Do you mean version 1.3.2 of *colorgcc*? The copy I have is that version and it doesn't have it. Can you provide a link? – Dennis Williamson Feb 23 '11 at 06:10
4

Many programs that generate colored output detect if they're writing to a TTY, and switch off colors if they aren't. This is because color codes are annoying when you only want to capture the text, so they try to "do the right thing" automatically.

The simplest way to capture color output from a program like that is to tell it to write color even though it's not connected to a TTY. You'll have to read the program's documentation to find out if it has that option.

The other option is to use a Pty instead of a pipe. In Perl, you can do this with IO::Pty::HalfDuplex or IO::Pty::Easy, both of which are higher-level wrappers around the low-level module IO::Pty.

cjm
  • 61,471
  • 9
  • 126
  • 175
  • So, a lot of people have suggested this solution, and it appears to be a great one. Thanks. But I couldn't figure out how to get it to work. Pty or the wrappers you mentioned are still quite complex, and the documentation is pretty weak unfortunately. – Alan Turing Feb 23 '11 at 06:09
3

The source code of ColorGCC is quite clear about this topic!

#! /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");
}

In addition to use a Pty instead of a pipe in Perl (as already pointed out by cjm) you can trick an application into thinking its stdin is interactive, not a pipe on the command line as well.

For example:

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

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

For further solutions see: bash: force exec'd process to have unbuffered stdout

Community
  • 1
  • 1
pete
  • 31
  • 1
  • Good answer. Right on. You identified the problem exactly. Not sure what versioning system that is for colorgcc, but I think the latest version is 1.3.2. At least that's what the original developer provides and also the Ubuntu package repository. As my second edit describes, this version actually has a key addition to the if statement you provided. Also, I should note that the `script -c` suggestion you made did not work for me in preserving color. – Alan Turing Feb 23 '11 at 06:08