68

Is there an elegant method in bash for running grep against a text file with two or more patterns, and each pattern that matches is output in a different color?

So a line that matches on MALE and AUGUST would output MALE in blue and AUGUST in orange? I am open to the use of sed, awk, grep and crayons or others.

NelsonGon
  • 13,015
  • 7
  • 27
  • 57
Evil Genius
  • 1,015
  • 2
  • 10
  • 16
  • 3
    `colout` may be helpful as colorizing solution: http://nojhan.github.io/colout/ `egrep 'MALE|AUGUST' file.txt | colout MALE blue | colout AUGUST yellow` or if you do not need to filter text stream, just highlight it, you can do it with `colout MALE blue – user2683246 Jun 19 '15 at 23:58
  • Also check the answers on https://stackoverflow.com/questions/981601/colorized-grep-viewing-the-entire-file-with-highlighted-matches – ndemou Oct 02 '17 at 15:00
  • see cgrep in https://unix.stackexchange.com/questions/104350/multicolored-grep and https://unix.stackexchange.com/a/104402/182661 – mosh May 15 '19 at 17:58

10 Answers10

79

You can cascade greps with different colors by specifying --color=always and using the regular expression 'foo|$' to pass all lines.

For example:

tail -f myfwlog | GREP_COLOR='01;36' egrep --color=always 'ssh|$' | GREP_COLOR='01;31' egrep -i --color=always 'drop|deny|$'

If you want the entire line to be highlighted, update your regular expression accordingly:

.... GREP_COLOR='01;31' egrep -i --color=always '^.*drop.*$|^.*deny.*$|$'
Rob Windsor
  • 806
  • 7
  • 2
  • This does not work if the same values are defined in `GREP_COLORS` (note the trailing `S`) - try unsetting it before: `$ GREP_COLORS=` (`GREP_COLOR` is deprecated) – Volker Siegel Oct 23 '14 at 11:20
  • 1
    To update the entire line, modifying the regex is the wrong approach. Instead, use `GREP_COLORS='sl=1;31' grep ...` and specify sl to color the whole line. – William Pursell May 07 '15 at 15:01
  • how to restrict the final output lines only to the line where a word is matched? Current command print all line, but color the word when found. I am looking for actual grep functionality in the output. – mtk Oct 05 '17 at 11:00
  • GREP_COLORS='sl=1;31' highlights the WHOLE LINE. Use mt= instead – anthony Mar 07 '19 at 01:11
  • @mtk : a bit late, but the answers colorizes but shows all lines. if you don't want "non matching lines", take out the "|$" from the regexps ($ matches all end of lines, hence all lines, and does not colorize anything there) – Olivier Dulac Mar 09 '22 at 14:47
  • if you want only to hilight matching line, you can put an egrep before all: tail -f myfwlog | egrep -i 'ssh|drop|deny' | GREP_COLOR='01;36' egrep --color=always 'ssh|$' | GREP_COLOR='01;31' egrep -i --color=always 'drop|deny|$' – Rudy Barbieri Sep 30 '22 at 12:40
35

grep is a regular expression matcher, not a syntax highlighter :). You'll have to use multiple invocations of grep, using a different value of GREP_COLOR for each.

GREP_COLOR="1;32" grep foo file.txt | GREP_COLOR="1;36" grep bar

That would highlight "foo" and "bar" in different colors in lines that match both. I don't think there is a (simple) way to handle all occurrences of either pattern, short of merging the output stream of two independent calls:

{ GREP_COLOR="1;32" grep foo file.txt
  GREP_COLOR="1;36" grep bar file.txt
} | ...

which will obviously look different than if there were a way to assign a separate color to each regular expression.


You can use awk to substitute each match with itself wrapped in the correct control code.

 echo "foo bar" | awk '{ gsub("bar", "\033[1;33m&\033[0m");
                         gsub("foo", "\033[1;36m&\033[0m"); print }'

In each line, you globally replace anything matching the given regular expression with itself (&) wrapped in the ANSI escape sequences for desired color (which grep --color does for you). After processing all of the possible matches, you need to explicitly print the line.

fedorqui
  • 275,237
  • 103
  • 548
  • 598
chepner
  • 497,756
  • 71
  • 530
  • 681
19

If you want something out of the box, you're probably looking for hhighlighter.

Here's an example:JBoss output

Take a look. It's incredibly useful for coloring words in different colors automatically. It's an impressive project that's built on top of ack.

Ehtesh Choudhury
  • 7,452
  • 5
  • 42
  • 48
  • 3
    h highlighter is a small Bash function which automates building a pipeline of `ack` commands and works in the same manner as the other two `grep`-based answers. – Dennis Williamson May 08 '14 at 21:00
8

I had the need to highlight strings in text files, and did not find a way that suited my needs, so I wrote a simple C program to colorize strings matching regular expressions.

You can download the source code from GitHub :

git clone http://github.com/mbornet-hl/hl

then :

cd hl/cr; make

Here is the usage :

hl: version 1.21
Usage: hl [-h|-eidD][-E][-rgybmcwRGYBMCW] regexp ...
  -h : help
  -v : version
  -u : do not bufferize output on stdout
  -e : extended regular expressions
  -i : ignore case
  -E : print on stderr
  -r : red
  -g : green
  -y : yellow
  -b : blue
  -m : magenta
  -c : cyan
  -w : white
  -R : red     (reverse video)
  -G : green   (reverse video)
  -Y : yellow  (reverse video)
  -B : blue    (reverse video)
  -M : magenta (reverse video)
  -C : cyan    (reverse video)
  -W : white   (reverse video)
  -d : debug
  -D : display regular expressions

To colorize the word "red" in red, and the word "blue" in blue, you just have to type in the following command :

echo "red blue red blue" | hl -r red -b blue

and here is an example to highlight the output of the ifconfig command :

hl -ei -m '^(eth|(vir)?br|vnet)[0-9.]*:[0-9]+\>'             \
       -b '^(eth|(vir)?br|vnet)[0-9.]*\.[0-9]+\>'            \
       -c '([0-9a-f]{2}:){5}[0-9a-f]{2}'                     \
       -g '\<UP\>|\<RUNNING\>|([0-9]{1,3}\.){3}[0-9]{1,3}\>' \
       -y '^(eth|(vir)?br|vnet)[0-9.:]*\>'                   \
       -W '[0-9a-f]{4}::[0-9a-f]{4}\:[0-9a-f]{4}:[0-9a-f]{4}:[0-9a-f]{4}' \
       -r ' (errors|dropped|overruns):[^0][0-9]*'

The order in which you specify the colors is important : if a string matches several regular expressions, the 1st one as a higher priority than the 2nd one, the 2nd one as a higher priority than the 3rd one, and so on ...

Hope This Helps.

Bush
  • 141
  • 1
  • 2
  • This has the best performance compared to the script based colouring solutions. Thanks! – Ray Nov 26 '18 at 20:53
4

Try the wonderful rpen http://github.com/rtulke/rpen

require egrep or grep

Now you need to copy the rpen.py file into the right folder and give it appropraite rights

cp rpen.py /usr/local/bin/rpen
chmod a+rx /usr/local/bin/rpen

then try

ps xau |rpen Ss "S\+" "\?\?" 
tripleee
  • 175,061
  • 34
  • 275
  • 318
cray
  • 145
  • 6
  • 1
    On cygwin, i've found the rpen installation very easy. I couldn't get ack and h to work. Usage example: `$ cat ~/.bashrc | rpen alias export` – Ellis Sep 16 '14 at 07:57
3

So here's the version(s) using sed. Let's say you want the word "FAILED" to be colored red, that would be:

sed 's/\(ERROR\)/\o033[31m\1\o033[39m/'

To have the whole line containing the word "FAILED" marked in red:

sed 's/\(.*ERROR.*\)/\o033[31m\1\o033[39m/'

To have multiple words marked red:

sed 's/\(ERROR\|FAILED\)/\o033[31m\1\o033[39m/'

To have multiple words and their wohle lines marked in red:

sed 's/\(.*FAILED.*\|.*ERROR.*\)/\o033[31m\1\o033[39m/'

To have multiple colors (ERROR=red/FAILED=blue)

sed -e 's/\(ERROR\)/\o033[31m\1\o033[39m/' -e 's/\(FAILED\)/\o033[34m\1\o033[39m/'

To use all this over ssh:

ssh user@host -t "tail -n 1024 -f /some/log/file | sed --unbuffered -e 's/\(.*ERROR.*\|.*FAILED.*\)/\o033[31m\1\o033[39m/' -e 's/\(.*postfix.*\|.*dhcpd.*\)/\o033[32m\1\o033[39m/'"

And for not having to type this everytime, just declare it as a function in your bashrc.

For other colors look up ANSI codes.

mikeymouse
  • 31
  • 1
1

How about this? You can do it with grep ONLY! Tricks here are (1) you take /OR/ of the words you are searching; (2) you use -A and -B options with large enough numbers (larger then the line count of your file).

echo "you make it" > tmp
echo "you take it" >> tmp
echo "you cake it" >> tmp
echo "you bake it" >> tmp
echo "you wake it" >> tmp

GREP_COLOR='1;32' grep -P "(take|cake|bake)" --color=always tmp | GREP_COLOR='1;33' grep -P "(cake|bake)" --color=always -A10000 -B10000 | GREP_COLOR='1;34' grep -P "(bake)" --color=always -A10000 -B10000 
user22097
  • 229
  • 3
  • 13
1

cxpgrep is right for this purpose - highlight patterns in different colors.

cxpgrep 'PAT1|PAT2|...' FILE_OR_DIRECTORY
treulz
  • 51
  • 1
1

My answer is below:

tailf logfile awk '/MALE/' {print "\033[34m" $0 "\033[39m"}  '/MALE/' {print "\033[33m" $0 "\033[39m"}
Leonardo Alves Machado
  • 2,747
  • 10
  • 38
  • 53
1

you can use bline (Beautiful Line Output) specifically designed for complex output and colorizing.

all lines, foreground red:

ls | bline -a red

all lines, foreground red, background green:

ls | bline -a red:green

all lines, foreground is default (NOTE.3), background is green:

ls | bline -a :green

add color-mode, odd lines (foreground) will be in light,italic,red

ls | bline -o light:red

add text-mode, as well

ls | bline -o light:italic:cross:red

combine options are okay:

ls | bline -a light:green -i red -I 7 -r yellow -R 5-10

using hex value, foreground in red

df | bline -H -a ff00ff

hex, foreground green, background red

df | bline -H -a 00ff00:ff0000

hex just background

df | bline -H -a :ffff00

look for string: 'sda' and make that/those line(s) green

lsblk | bline -m sda light:green

some screenshots

enter image description here

2. enter image description here

3. enter image description here

4. enter image description here

5. enter image description here

Shakiba Moshiri
  • 21,040
  • 2
  • 34
  • 44