Grep – How to Filter Output of Command by Color

colorsfiltergreptext;

I am running a utility that doesn't offer a way to filter its output. Nothing in the text of the output indicates that a particular function failed but it does show in red. The output is so long that at the end when it reports some # of errors I can't always scroll to see the output where the error occurred.

How can I filter out non-red text?

pseudo code:

dolongtask | grep -color red

Edit

The command outputs other colors as well and I need to be able to filter out all text that isn't red. Also the text coloring is multiline.

Best Answer

Switching the color is done through escape sequences embedded in the text. Invariably, programs issue ANSI escape sequences, because that's what virtually all terminals support nowadays.

The escape sequence to switch the foreground color to red is \e[31m, where \e designates an escape character (octal 033, hexadecimal 1b, also known as ESC, ^[ and various other designations). Numbers in the range 30–39 set the foreground color; other numbers set different attributes. \e[0m resets all attributes to their default value. Run cat -v to check what the program prints, it might use some variant such as \e[0;31m to first reset all attributes, or \e[3;31 to also switch italics on (which many terminals don't support).

In ksh, bash or zsh, you can use $'…' to enable backslash escapes inside the quotes, which lets you type $'\e' to get an escape character. Note that you will then have to double any backslash that you want to pass to grep. In /bin/sh, you can use "$(printf \\e)" or type a literal escape character.

With the GNU grep -o option, the following snippet filters red text, assuming that it starts with the escape sequence \e[31m, ends with either \e[0m or \e[30m on the same line, and contain no embedded escape sequence.

grep -Eo $'\e\\[31m[^\e]*\e\\[[03]?m'

The following awk snippet extracts red text, even when it's multiline.

awk -v RS='\033' '
    match($0, /^\[[0-9;]*m/) {
        color = ";" substr($0, 2, RLENGTH-2) ";";
        $0 = substr($0, RLENGTH+1);
        gsub(/(^|;)0*[^03;][0-9]*($|;)/, ";", color);
        red = (color ~ /1;*$/)
    }
    red'

Here's a variation which retains the color-changing commands, which could be useful if you're filtering multiple colors (here red and magenta).

awk -v RS='\033' '
    match($0, /^\[[0-9;]*m/) {
        color = ";" substr($0, 2, RLENGTH-2) ";";
        printf "\033%s", substr($0, 1, RLENGTH);
        $0 = substr($0, RLENGTH+1);
        gsub(/(^|;)0*[^03;][0-9]*($|;)/, ";", color);
        desired = (color ~ /[15];*$/)
    }
    desired'
Related Question