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 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.

share|improve this question
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 at 1:28

2 Answers 2

up vote 12 down vote accepted

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

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.

share|improve this answer
    
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 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 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. –  ogc-nick Jun 6 at 2:38

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.