22

When using grep --color=always I can get pretty color highlighting for regex matches.

However, grep only returns lines with at least one match. Instead, I am looking for a way to simply highlight regex matches, while leaving all other input alone, without dropping lines without any matches.

I have tried to get color working with sed, and read the grep documentation, but I can't seem to get what I want.

In case my description isnt obvious, I want:

INPUT:

  • fred
  • ted
  • red
  • lead

Regex:

  • ".*red"

OUTPUT:

  • fred ( in red )
  • ted
  • red ( in red )
  • lead

So that I could do:

list_stuff | color_grep "make_this_stand_out_but_dont_hide_the_rest"

EDIT:

I have found a solution, which isn't pretty, but it works:

Thanks to: http://www.pixelbeat.org/docs/terminal_colours/

Particularly the script (which I modified/simplified): http://www.pixelbeat.org/talks/iitui/sedgrep

function sedgrep ()
{
    C_PATT=`echo -e '\033[33;01m'`
    C_NORM=`echo -e '\033[m'`

    sed -s "s/$1/${C_PATT}&${C_NORM}/gi"
}

Still looking for an easier way to do this!

mmocny
  • 8,775
  • 7
  • 40
  • 50

7 Answers7

22

The simplest solution would be to use egrep --color=always 'text|^' which would match all line beginnings but only color the desired text.

crenate
  • 3,370
  • 2
  • 20
  • 13
9

Here is a script I use to colorize output.

I think I found the idea/snippet on some kind of blog or bash/sed tutorial - can't find it anymore, it was very long time ago.

#!/bin/bash

red=$(tput bold;tput setaf 1)            
green=$(tput setaf 2)                    
yellow=$(tput bold;tput setaf 3)         
fawn=$(tput setaf 3)
blue=$(tput bold;tput setaf 4)           
purple=$(tput setaf 5)
pink=$(tput bold;tput setaf 5)           
cyan=$(tput bold;tput setaf 6)           
gray=$(tput setaf 7)                     
white=$(tput bold;tput setaf 7)          
normal=$(tput sgr0)                      

sep=`echo -e '\001'` # use \001 as a separator instead of '/'

while [ -n "$1" ] ; do
  color=${!1}
  pattern="$2"
  shift 2

  rules="$rules;s$sep\($pattern\)$sep$color\1$normal${sep}g"
done

#stdbuf -o0 -i0 sed -u -e "$rules"
sed -u -e "$rules"

Usage:

./colorize.sh color1 pattern1 color2 pattern2 ...

e.g.

dmesg | colorize.sh red '.*Hardware Error.*' red 'CPU[0-9]*: Core temperature above threshold' \
green 'wlan.: authenticated.*' yellow 'wlan.: deauthenticated.*'

Doesn't work well with overlapping patterns, but I've found it very useful anyway.

HTH

Pawel Wiejacha
  • 704
  • 7
  • 5
  • I had to replace `sep='/'` otherwise an error has been shown but after that it works beautifully! Thank you! – Kia Kaha Sep 07 '21 at 23:09
3

This little function works well in my ZShell:

function color_grep {
    sed s/$1/$fg[yellow]$1$terminfo[sgr0]/g
}

(Needs

autoload colors zsh/terminfo

)

Maybe you can do something similar?

Edit: Sorry, this won't work with regexes. You will have to tweak it a bit ...

boxxar
  • 11,093
  • 2
  • 20
  • 7
  • I am using bash, but that looks to be nearly the same type of solution as the one I posted. Thanks! – mmocny Oct 27 '08 at 21:08
1

The way you're doing this now is probably about as clean as you can expect to make this, unless of course you write your own grep tool. If you don't necessarily care about preserving the order of the output, here's the other way I can think of to do this:

function colormatch ()
{
    tee - | grep --color=always $1 | sort | uniq
}

Not as efficient as using sed (more processes created, and tee-ing the output), so I'd probably recommend sticking with your sed solution.

Jay
  • 41,768
  • 14
  • 66
  • 83
  • Good point. Your solution has a syntax which is easier to remember, for those times when I don't have my .bashrc file handy :) – mmocny Oct 27 '08 at 22:42
1

You could use the -C<num> option to grep which shows you <num> lines of context around your match. Just make sure <num> is as least as large as the number of lines in your file.

PiedPiper
  • 5,735
  • 1
  • 30
  • 40
  • oooh, clever abuse of the context option, I like it :-) – Jay Oct 28 '08 at 02:43
  • 1
    Actually, I tried this, and it didnt work for three reasons: One, if their was not a single match, it wouldnt return anything. Two, it was slow when was really large. Three (biggest problem), results get printed with each "match", so if I am running this command on a pipe I get output in blobs – mmocny Oct 28 '08 at 15:55
  • However, it is a very clever way to do it as a quick hack, especially for small results if you know their is a hit :) I do this all the time when not on my own PC – mmocny Oct 28 '08 at 15:56
0

I'm digging this little python utility. If not on debian, use alien to convert to rpm.

http://korpus.juls.savba.sk/~garabik/software/grc.html

regexp=.*red
colours="\033[38;5;160m"
count=once

This is a nice page on terminal colors.

http://www.pixelbeat.org/docs/terminal_colours/

(Queen's english is so colourful.)

TJR
  • 3,617
  • 8
  • 38
  • 41
0

I recently made something similar as a filter. I use it to color the "headers" in a tail with multiple files, like this:

tail -f access.log error.log foo.log | logcol.sh

The headers look like this:

==> access.log <==

I got confused by the quick changes between the different logfiles, so this logcol.sh helps. The ==> is hardcoded for the specific usage but could be a parameter as well.

#!/bin/sh
while read line
do
  if test `expr "$line" : "==>.*"`  -eq 0 ;
  then
    printf '\033[0m%s\n' "$line"
  else
    printf '\033[0;31m%s\n' "$line"
  fi
done

Maybe not the most elegant but I think it's quite readable. I hope I don't have any typos ;-) HTH, rob