Filter output of command by color

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'

You can have grep look for control characters, some of which are responsible for making the pretty colors on the terminal.

dolongtask | grep '[[:cntrl:]]'

For example, this echoes a red "test" into grep, which finds it due to it being surrounded by control characters:

$ echo -e '\033[00;31mtest\033[00m' | grep --color=none '[[:cntrl:]]'
test     <-- in red

The --color=none is just to make sure grep does not apply its own colorization to the matched output, but prints the whole line faithfully so that control characters will be interpreted by the shell.