12

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.

  • 1
    I apologize for asking the obvious - but all output is on >&1? I mean, the red stuff doesn't go away if you 2>/dev/null, right? – mikeserv Jun 6 '14 at 1:28
15

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'
  • The awk solution worked for me. Any way to retain color in output? – km6zla Jun 6 '14 at 2:47
  • @ogc-nick You want all-red output? printf '\e[31m'; awk …; printf '\e[0m' – Gilles Jun 6 '14 at 2:58
  • Just for whatever color I am filtering for to be retained in the output. – km6zla Jun 6 '14 at 3:30
  • @ogc-nick See my edit. – Gilles Jun 6 '14 at 9:23
6

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.

  • Nice. I wonder if one could go further and do something like grep -E $'\033\[0?[01];31m.+?\033\[0?0m' or grep -Po '\033\[0?[01]+;31m\K.+?(?=\033\[0?0m)' to test for red specifically? – steeldriver Jun 6 '14 at 1:04
  • I started coming up with regexes like the ones you suggest, but before I could get them to work I stumbled upon [[:cntrl:]]. I tested yours and they work for me, ie. matching red and failing to match other colors. – savanto Jun 6 '14 at 1:10
  • Works great but will match any color. I didn't mention it in the question but many other colors are also output and I just want to see the red stuff. +1 for simple and working code. – km6zla Jun 6 '14 at 2:38

Your Answer

By clicking "Post Your Answer", you acknowledge that you have read our updated terms of service, privacy policy and cookie policy, and that your continued use of the website is subject to these policies.

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