Take the 2-minute tour ×
Unix & Linux Stack Exchange is a question and answer site for users of Linux, FreeBSD and other Un*x-like operating systems.. It's 100% free, no registration required.

I am running some unit test code. The unit test code outputs regular text. There is a lot of the text so I want to highlight for the user important keywords.

In this case the keywords are "PASS" and "FAIL".

How do you colorize "PASS" to be green and "FAIL" to be red?

share|improve this question
    
Have you thought about supressing stdout/stderr of tests that pass, and only printing stdout/stderr for failed tests? PHPUnit does this, or is configurable to do this. I've found that this approach works quite well, YMMV. –  Clayton Stanley Aug 30 '12 at 2:44

4 Answers 4

up vote 2 down vote accepted

Here is a general-purpose script to colorize regex patterns (probably needs some retouching):

#!/bin/sh

color_to_num () {
    case $1 in
        black)  echo 0;;
        red)    echo 1;;
        green)  echo 2;;
        yellow) echo 3;;
        blue)   echo 4;;
        purple) echo 5;;
        cyan)   echo 6;;
        white)  echo 7;;
        *)      echo 0;;
    esac
}

# default values for foreground and background colors
bg=
fg=

while getopts f:b: option; do
    case "$option" in
        f) fg="$OPTARG";;
        b) bg="$OPTARG";;
    esac
done

shift $(($OPTIND - 1))

pattern="$*"

if [ -n "$fg" ]; then
    fg=$(tput setaf $(color_to_num $fg))
fi
if [ -n "$bg" ]; then
    bg=$(tput setab $(color_to_num $bg))
fi

if [ -z "$fg$bg" ]; then
    fg=$(tput smso)
fi

sed "s/$pattern/$(tput bold)${fg}${bg}&$(tput sgr0)/g"

Name it hilite.sh and use it this way:

$ ./BIN_PROGRAM | hilite.sh -f green PASS | hilite.sh -f red FAIL

$ # Here is an example one liner
$ echo -e "line 1: PASS\nline 2: FAIL" | hilite.sh -f green PASS | hilite.sh -f red FAIL
share|improve this answer
    
I made this community wiki because my solution doesn't work. –  Trevor Boyd Smith Aug 28 '12 at 21:45
    
An easy way to test is to make another script that echos text with the keywords "PASS" and "FAIL". That would then be called by this script. –  Trevor Boyd Smith Aug 28 '12 at 21:46
    
Slightly off topic, but I have to point out that parsing the output of $BIN via eval into command substitution is probably fragile, cumbersome, and downright dangerous. STDOUT is a stream and is generally meant to be processed by stream like tools. Trying to capture an entire stream in a variable is not very efficient. Also, eval is dangerously powerful, so only use it if you really know what you are doing. –  jw013 Aug 28 '12 at 22:15
    
I replaced the script with a working one. –  angus Aug 28 '12 at 22:30
    
@angus, really really good and impressive script IMO. –  Trevor Boyd Smith Aug 29 '12 at 13:25

Embedding arbitrary strings (like tput output) into sed replace expressions is problematic because you have to ensure (by escaping) the string is valid sed syntax, which is more complexity that is best avoided. I would use awk instead. Just as an example:

{ echo line 1: PASS; echo line 2: FAIL; } | 
    awk -v "red=$(tput setaf 1)" -v "green=$(tput setaf 2)" \
        -v "reset=$(tput sgr0)" '
    { for (i = 1; i <= NF; i++) {
           if ($i == "FAIL") printf "%s", red "FAIL" reset;
           else if ($i == "PASS") printf "%s", green "PASS" reset;
           else printf "%s", $i

           if (i == NF) printf "%s", ORS
           else printf "%s", OFS 
      }}'

The key is to assign the tput sequences to awk variables, done here using the -v options.

share|improve this answer
    
I ran your program and it does what is needed. Huzzah! But unfortunately I am not an awk guru so I find it hard to understand what is going on. Correct me where I am wrong. For each new line awk is run? The for loop in the awk source code is doing: "for all tokens in the line"? then it processes each token? –  Trevor Boyd Smith Aug 29 '12 at 13:16
    
Awk thinks in terms of fields and records. By default, records are lines of text and fields are non-whitespace. An awk script consists of a set of blocks (the stuff between the {} at the top level). Each block is associated with a condition - in my post above there is none because I want to unconditionally act on every input line. Awk operates by reading records from files or standard input (in this case it is STDIN b/c no file names were provided), and matching them against every block in order, executing the block on the record if the condition matches. –  jw013 Aug 29 '12 at 13:51

supercat seems to do what you're looking for.

Package: supercat
Description-en: program that colorizes text for terminals and HTML
 Supercat is a program that colorizes text based on matching regular
 expressions/strings/characters. Supercat supports html output as well
 as standard ASCII text. Unlike some text-colorizing programs that
 exist, Supercat does not require you to have to be a programmer to
 make colorization rules.
Homepage: http://supercat.nosredna.net/

There doesn't seem to be any way to tell it what to colourise on the command line, you have to specify a config file.

I seem to recall there used to be a program called 'hilite' or 'hl' that highlighted text that matched a pattern (like grep --colour, but displaying non-matching lines too), but I couldn't find it when I searched for it.

Finally, GNU grep can be used to highlight patterns - but only one colour can be used (i.e. you can't have PASS in green and FAIL in red, both would be highlighted with the same colour).

Pipe your data through something like this:

egrep --color "\b(PASS|FAIL)\b|$"

This example uses egrep (aka grep -E), but -G basic regexp, -F fixed-string, and -P PCRE also work.

All matches will be highlighted. Default is red, or set the GREP_COLOR env var.

The key to this working is that the final |$ in the pattern matches end-of-line (i.e. all lines match) so all lines will be displayed (but not colorised).

The \b are word-boundary markers so that it matches e.g. FAIL but not FAILURE. they're not necessary, so remove them if you want to match partial words.

Here's the example wrapper script for supercat that I wrote yesterday. It works, but in writing it, I discovered that supercat doesn't have any option for case-insensitive searches. IMO, that makes the program significantly less useful. It did, however, greatly simplify the script because I didn't have to write a '-i' option :)

#! /bin/bash 

# Requires: tempfile from debian-utils, getopt from util-linux, and supercat

SCRIPTNAME=$(basename $0)
CFGFILE=$(tempfile -p spc)

usage() {
  cat <<__EOF__
Highlight regexp patterns found on stdin or files specified on command
line with specified colours.

Usage: $SCRIPTNAME [ --colour "pattern" ...] [FILE]

Options:

        -k,--black   regexp
        -r,--red     regexp
        -g,--green   regexp
        -y,--yellow  regexp
        -b,--blue    regexp
        -m,--magenta regexp
        -c,--cyan    regexp
        -w,--white   regexp

Example:

    run-script.sh | $SCRIPTNAME --green PASS --red FAIL

__EOF__
  exit 0
}


# Format definition from the spc man page:
#1234567890123456789012345678901234567890123456789012345
#HTML Color Name      Col A N T RE / String / Characters
FMT="%-20s %3s %1s %1s %1s (%s)\n"

add_color_to_config() {
  COLOR="$1"
  PATTERN="$2"

  printf "$FMT" "$COLOR" "$COLOR" - 0 r "$PATTERN" >> "$CFGFILE"
}


# uses the "getopt" program from util-linux, which supports long
# options. The "getopts" built-in to bash does not.
TEMP=$(getopt \
       -o 'hk:r:g:y:b:m:c:w:' \
       -l 'help,black:,red:,green:,yellow:,blue:,magenta:,cyan:,white:' \
       -n "$0" -- "$@")

if [ $? != 0 ] ; then echo "Terminating..." >&2 ; exit 1 ; fi

eval set -- "$TEMP"

while true ; do
    case "$1" in
        -k|--bla*)       add_color_to_config blk "$2" ; shift 2 ;;
        -r|--red)        add_color_to_config red "$2" ; shift 2 ;;
        -g|--gre*)       add_color_to_config grn "$2" ; shift 2 ;;
        -y|--yel*)       add_color_to_config yel "$2" ; shift 2 ;;
        -b|--blu*)       add_color_to_config blu "$2" ; shift 2 ;;
        -m|--mag*)       add_color_to_config mag "$2" ; shift 2 ;;
        -c|--cya*)       add_color_to_config cya "$2" ; shift 2 ;;
        -w|--whi*)       add_color_to_config whi "$2" ; shift 2 ;;

        -h|--hel*)       usage ; exit 0 ;;

        --)         shift ; break ;;

        *)          echo 'Unknown option!' ; exit 1 ;;
    esac
done

spc -R -c "$CFGFILE" "$@"
rm -f "$CFGFILE"
share|improve this answer
1  
BTW, if you used supercat you could modify the script you posted to generate a temporary config file based on the command-line args, and then call spc -c /path/to/your/temp/config. This would allow you to easily specify different colours for different patterns on the command line. –  cas Aug 28 '12 at 23:07
    
also btw, i wrote a simple wrapper script to do that. i have to head in to work now but i'll clean it up, add some comments, etc and post it later today. –  cas Aug 29 '12 at 0:05

use printf:

printf "\e[%sm%s\e[00m\n" <some_number> <text_in_colour>

eg.

printf "\e[%sm%s\e[00m\n" 32 yodle

the last \n adds the newline character.

to see possible values of try something like:

for i in {0..9} {30..38} {90..98} {100..108};
do
    printf "%d:\e[%sm%s\e[00m\n" $i "$i" yodle;
done

besides colour you can add modifiers like bold or underline or coloured text with coloured background by combining the numbers. To create blue text with grey background that is underlined and striked through use:

printf "\e[%sm%s\e[00m\n" "4;9;34;107" yodle

Cheers,

/B2S

share|improve this answer

Your Answer

 
discard

By posting your answer, you agree to the privacy policy and terms of service.

Not the answer you're looking for? Browse other questions tagged or ask your own question.