zslayton
zslayton

Reputation: 52805

Colorized grep -- viewing the entire file with highlighted matches

I find grep's --color=always flag to be tremendously useful. However, grep only prints lines with matches (unless you ask for context lines). Given that each line it prints has a match, the highlighting doesn't add as much capability as it could.

I'd really like to cat a file and see the entire file with the pattern matches highlighted.

Is there some way I can tell grep to print every line being read regardless of whether there's a match? I know I could write a script to run grep on every line of a file, but I was curious whether this was possible with standard grep.

Upvotes: 704

Views: 334942

Answers (23)

Al Mamun
Al Mamun

Reputation: 1056

I use following command for similar purpose:

grep -C 10 searchtext file

grep will print 10 * 2 lines, before & after of the highlighted search text.

Upvotes: 2

Abhishek Jaisingh
Abhishek Jaisingh

Reputation: 1730

The -z option for grep is also pretty slick!

cat file1 | grep --color -z "pattern"

Here's the documentation for the -z option:

-z, --null-data

Treat input and output data as sequences of lines, each terminated by a zero byte (the ASCII NUL character) instead of a newline. Like the -Z or --null option, this option can be used with commands like sort ‐z to process arbitrary file names.

Upvotes: 23

F. Hauri  - Give Up GitHub
F. Hauri - Give Up GitHub

Reputation: 70792

The way

Preamble

This answer use GNU sed. This work with very old version of this tool (tested using 2012 version of sed (GNU sed) 4.2.2, but this will work with older versions too.)

Unfortunately if on MacOS, you may have to install them maybe by using brew.

Power and flexibility of sed solution

As there is already a lot of different solution, but none did shown sed as solution,
and because sed is lighter and quicker than grep, I prefer to use sed for this kind of job:

sed 's/pattern/\o33[47;31;1m&\o033[0m/' file

This seems less intuitive.

  • s/pattern/replaced/ is sed replacement command to replace pattern by replaced.
  • \o33 is the sed syntax to generate the character octal 033 -> Escape.
    (Some shells and editors also allow entering <Ctrl>-<V> followed by <Esc>, to type the character directly.)
  • Esc [ 47 ; 31 ; 1 m is an ANSI escape code: Background grey, foreground red and bold face.
  • & will re-print the pattern.
  • Esc [ 0 m returns the colors to default.

You could also highlight the entire line, but mark the pattern as red:

sed -E <file -e \
    's/^(.*)(pattern)(.*)/\o33[30;47m\1\o33[31;1m\2\o33[0;30;47m\3\o33[0m/'

Dynamic tail -f, for real-time following logfiles

One of advantage of using sed: You could send a alarm beep on console, using bell ascii character 0x7. I often use sed like:

sudo tail -f /var/log/kern.log |
    sed -ue 's/[lL]ink .*\([uU]p\|[dD]own\).*/\o33[47;31;1m&\o33[0m\o7/'

Or, if using systemd:

sudo journalctl -akf |
    sed -ue 's/[lL]ink .*\([uU]p\|[dD]own\).*/\o33[47;31;1m&\o33[0m\o7/'
  • -u stand for unbuffered. This ensure that line will be treated immediately.

So I will hear some beep instantly, when I connect or disconnect my ethernet cable.

Of course, instead of link up pattern, you could watch for USB in same file, or...

Advantage of sed

For sample: You're Charlie, awaiting some mail from Alice or Bob, transiting by your mail server:

So you have to search for from=.*\(alice\|bob\)@someserver.org in /var/log/mail.log. As sed is a language, you could use many directives, for sample: avoiding imap and pop logs while watching for incoming request on some mail server:

tail -f /var/log/mail.log | sed -ue '
    /[[:space:]]\(imap\|pop\)d\[/d;
    /[^[:alnum:]]smtpd\[/{ 
        s/.*/\o33[30;47m&\o33[0m/;
        s/\(alice\|bob\)@someserver.org/\o33[31;1m\o7&\o33[30m/g;
    }
'

This will

  • delete lines regarding imap or pop servers.
    ( lines containing imapd[ or popd[ ),
  • highlight lines regarding smtp server
    ( lines containing smtpd[ ) and
  • for lines containing smtpd[, highlight and beep when line containing [email protected] or [email protected] is found.
  • Then print all not deleted lines (as -n switch was not used).

Upvotes: 6

ephemient
ephemient

Reputation: 204718

I'd like to recommend ack -- better than grep, a power search tool for programmers.

$ ack --color --passthru --pager="${PAGER:-less -R}" pattern files
$ ack --color --passthru pattern files | less -R
$ export ACK_PAGER_COLOR="${PAGER:-less -R}"
$ ack --passthru pattern files

I love it because it defaults to recursive searching of directories (and does so much smarter than grep -r), supports full Perl regular expressions (rather than the POSIXish regex(3)), and has a much nicer context display when searching many files.

Upvotes: 53

Achal Neupane
Achal Neupane

Reputation: 5719

Also try:

egrep 'pattern1|pattern2' FILE.txt | less -Sp 'pattern1|pattern2'

This will give you a tabular output with highlighted pattern/s.

Upvotes: 0

Serious Angel
Serious Angel

Reputation: 1555

As grep -E '|pattern' has already been suggested, just wanted to clarify that it's possible to highlight a whole line too.

For example, tail -f somelog | grep --color -E '| \[2\].*' (specifically, the part -E '|):

Upvotes: 14

Ryan Oberoi
Ryan Oberoi

Reputation: 14495

Here are some ways to do it:

grep --color 'pattern\|$' file
grep --color -E 'pattern|$' file
egrep --color 'pattern|$' file

The | symbol is the OR operator. Either escape it using \ or tell grep that the search text has to be interpreted as regular expressions by adding -E or using the egrep command instead of grep.

The search text "pattern|$" is actually a trick, it will match lines that have pattern OR lines that have an end. Because all lines have an end, all lines are matched, but the end of a line isn't actually any characters, so it won't be colored.

To also pass the colored parts through pipes, e.g. towards less, provide the always parameter to --color:

grep --color=always 'pattern\|$' file | less -r
grep --color=always -E 'pattern|$' file | less -r
egrep --color=always 'pattern|$' file | less -r

Upvotes: 1086

Bruce Edge
Bruce Edge

Reputation: 2189

Use ripgrep, aka rg: https://github.com/BurntSushi/ripgrep

rg --passthru...

Color is the default:

enter image description here

  rg -t tf -e  'key.*tfstate' -e dynamodb_table
       --passthru
       Print both matching and non-matching lines.

       Another way to achieve a similar effect is by modifying your pattern to
       match the empty string. 
       For example, if you are searching using rg foo then using 
       rg "^|foo" instead will emit every line in every file searched, but only
       occurrences of foo will be highlighted. 
       This flag enables the same behavior without needing to modify the pattern.

Sacrilege, granted, but grep has gotten complacent.

brew/apt/rpm/whatever install ripgrep

You'll never go back.

Upvotes: 6

Dennis Williamson
Dennis Williamson

Reputation: 360085

Here's something along the same lines. Chances are, you'll be using less anyway, so try this:

less -p pattern file

It will highlight the pattern and jump to the first occurrence of it in the file.

You can jump to the next occurence with n and to the previous occurence with p. Quit with q.

Upvotes: 134

Serhii Nadolynskyi
Serhii Nadolynskyi

Reputation: 5563

It might seem like a dirty hack.

grep "^\|highlight1\|highlight2\|highlight3" filename

Which means - match the beginning of the line(^) or highlight1 or highlight2 or highlight3. As a result, you will get highlighted all highlight* pattern matches, even in the same line.

Upvotes: 1

dimo414
dimo414

Reputation: 48824

Here's my approach, inspired by @kepkin's solution:

# Adds ANSI colors to matched terms, similar to grep --color but without
# filtering unmatched lines. Example:
#   noisy_command | highlight ERROR INFO
#
# Each argument is passed into sed as a matching pattern and matches are
# colored. Multiple arguments will use separate colors.
#
# Inspired by https://stackoverflow.com/a/25357856
highlight() {
  # color cycles from 0-5, (shifted 31-36), i.e. r,g,y,b,m,c
  local color=0 patterns=()
  for term in "$@"; do
    patterns+=("$(printf 's|%s|\e[%sm\\0\e[0m|g' "${term//|/\\|}" "$(( color+31 ))")")
    color=$(( (color+1) % 6 ))
  done
  sed -f <(printf '%s\n' "${patterns[@]}")
}

This accepts multiple arguments (but doesn't let you customize the colors). Example:

$ noisy_command | highlight ERROR WARN

Upvotes: 0

treulz
treulz

Reputation: 71

To highlight patterns while viewing the whole file, h can do this.

Plus it uses different colors for different patterns.

cat FILE | h 'PAT1' 'PAT2' ...

You can also pipe the output of h to less -R for better reading.

To grep and use 1 color for each pattern, cxpgrep could be a good fit.

Upvotes: 3

kepkin
kepkin

Reputation: 1145

You can use my highlight script from https://github.com/kepkin/dev-shell-essentials

It's better than grep because you can highlight each match with its own color.

$ command_here | highlight green "input" | highlight red "output"

Screen shot from Github project

Upvotes: 34

uronce
uronce

Reputation: 145

I added this to my .bash_aliases:

highlight() {
  grep --color -E "$1|\$"
}

Upvotes: 9

Edoot
Edoot

Reputation: 421

If you want highlight several patterns with different colors see this bash script.

Basic usage:

echo warn error debug info 10 nil | colog

You can change patterns and colors while running pressing one key and then enter key.

Upvotes: 0

Andrew Magee
Andrew Magee

Reputation: 6684

Alternatively you can use The Silver Searcher and do

ag <search> --passthrough

Upvotes: 1

user2683246
user2683246

Reputation: 3568

Use colout program: http://nojhan.github.io/colout/

It is designed to add color highlights to a text stream. Given a regex and a color (e.g. "red"), it reproduces a text stream with matches highlighted. e.g:

# cat logfile but highlight instances of 'ERROR' in red
colout ERROR red <logfile

You can chain multiple invocations to add multiple different color highlights:

tail -f /var/log/nginx/access.log | \
    colout ' 5\d\d ' red | \
    colout ' 4\d\d ' yellow | \
    colout ' 3\d\d ' cyan | \
    colout ' 2\d\d ' green

Or you can achieve the same thing by using a regex with N groups (parenthesised parts of the regex), followed by a comma separated list of N colors.

vagrant status | \
    colout \
        '\''(^.+  running)|(^.+suspended)|(^.+not running)'\'' \
        green,yellow,red

Upvotes: 21

Fabien Sa
Fabien Sa

Reputation: 9480

You can also create an alias. Add this function in your .bashrc (or .bash_profile on osx)

function grepe {
    grep --color -E "$1|$" $2
}

You can now use the alias like this: "ifconfig | grepe inet" or "grepe css index.html".

(PS: don't forget to source ~/.bashrc to reload bashrc on current session)

Upvotes: 23

fly_a320
fly_a320

Reputation: 21

another dirty way:

grep -A80 -B80 --color FIND_THIS IN_FILE

I did an

alias grepa='grep -A80 -B80 --color'

in bashrc.

Upvotes: 2

MarkHu
MarkHu

Reputation: 1849

One other answer mentioned grep's -Cn switch which includes n lines of Context. I sometimes do this with n=99 as a quick-and-dirty way of getting [at least] a screenfull of context when the egrep pattern seems too fiddly, or when I'm on a machine on which I've not installed rcg and/or ccze.

I recently discovered ccze which is a more powerful colorizer. My only complaint is that it is screen-oriented (like less, which I never use for that reason) unless you specify the -A switch for "raw ANSI" output.

+1 for the rcg mention above. It is still my favorite since it is so simple to customize in an alias. Something like this is usually in my ~/.bashrc:

alias tailc='tail -f /my/app/log/file | rcg send "BOLD GREEN" receive "CYAN" error "RED"'

Upvotes: 1

Reputation:

I use rcg from "Linux Server Hacks", O'Reilly. It's perfect for what you want and can highlight multiple expressions each with different colours.

#!/usr/bin/perl -w
#
#       regexp coloured glasses - from Linux Server Hacks from O'Reilly
#
#       eg .rcg "fatal" "BOLD . YELLOW . ON_WHITE"  /var/adm/messages
#
use strict;
use Term::ANSIColor qw(:constants);

my %target = ( );

while (my $arg = shift) {
        my $clr = shift;

        if (($arg =~ /^-/) | !$clr) {
                print "Usage: rcg [regex] [color] [regex] [color] ...\n";
                exit(2);
        }

        #
        # Ugly, lazy, pathetic hack here. [Unquote]
        #
        $target{$arg} = eval($clr);

}

my $rst = RESET;

while(<>) {
        foreach my $x (keys(%target)) {
                s/($x)/$target{$x}$1$rst/g;
        }
        print
}

Upvotes: 9

Wernsey
Wernsey

Reputation: 5491

Here is a shell script that uses Awk's gsub function to replace the text you're searching for with the proper escape sequence to display it in bright red:

#! /bin/bash
awk -vstr=$1 'BEGIN{repltext=sprintf("%c[1;31;40m&%c[0m", 0x1B,0x1B);}{gsub(str,repltext); print}' $2

Use it like so:

$ ./cgrep pattern [file]

Unfortunately, it doesn't have all the functionality of grep.

For more information , you can refer to an article "So You Like Color" in Linux Journal

Upvotes: 1

nik
nik

Reputation: 13450

Ok, this is one way,

wc -l filename

will give you the line count -- say NN, then you can do

grep -C NN --color=always filename

Upvotes: 1

Related Questions