20

I was wondering if there is a program in the common unix toolset such as grep that instead of filtering the lines that contain a string, simply outputs the same input but highlighting or coloring the selected string.

I was thinking in doing it by myself (should be simple enough), but maybe it already exists as a unix command.

I'm planning in using it to monitor logs, so I would do something like this:

tail -f logfile.log | highlight "error"

Usually when I'm monitoring logs I need to find a particular string but I also need to know what is written before and after the string, so filtering sometimes is not enough.

Does something like that exist?

Thanks

14 Answers 14

23

This is a funny trick for it with the basic grep command. It consists in using two filters: the one you want to apply and a dummy one that matches all the lines but produces no highlight. This dummy match can be either ^ (beginning of line) or $ (end of line).

grep "^\|text" --color='always' file

or

grep -E "^|text" --color='always' file

See an example:

$ cat a
hello this is 
some text i wanted
to share with you
$ grep "^\|text" --color='always' a
hello this is 
some text i wanted     # "text" is highlighted
to share with you
  • 3
    It looks like you don't even need ^ or $ and just "|text" works as well. – musiphil Dec 5 '14 at 19:07
7

There is a tool named ack. You can find it at http://beyondgrep.com and it is indeed a tool beyond grep. Its most common use is the filling of that role of find . -name "*.java" --print | xargs grep clazz or the like. Because we do that all the time.

Just ack clazz and you get the output. Searches the proper files (doesn't bother trying to grep binaries) and gives a nice color output too.

If you use it with the --passthru option it will print the entire input stream, highlighting the matched regions in color.

--passthru Print all lines, whether matching or not

As the documentation states if - is used for the file, it will take STDIN:

If any files or directories are specified, then only those files and directories are checked. ack may also search STDIN, but only if no file or directory arguments are specified, or if one of them is "-".

Thus, pardon the cat abuse (and the pun - see below) you can have it :

$ cat file | ack --passthru pattern
$ cat file | ack --passthru pattern -

This will take the output of the pipe, and send it through ack which will print all the lines (with --passthru) with the pattern being highlighted.

This is exactly the tool you are after (and a bit more). It is a standard package for many package managers. See http://beyondgrep.com/install/ for your favorite.

_   /|
\'o.O'
=(___)=
   U    ack --thpppt!

(If you don't recognize it, thats Bill the Cat though the image search might also help - don't click on the Miley Cyrus set)

  • Ack is a great tool but how can it print its entire input simply highlighting the search pattern? It is normally used as a form of targeted grep -R as you explain, I don't see how it can help the OP. – terdon Dec 6 '14 at 12:11
  • @terdon the --passthru option will print all the lines and highlight the patterns of interest. Likewise, for working on STDIN, one can use the hyphen as the file 'name', or no file arguments at all. – user450 Dec 6 '14 at 18:41
  • I see, that makes sense. Since you'd said it acts like grep, I assumed it would only print the matching line. – terdon Dec 7 '14 at 0:28
  • @terdon its default mode is grepy. Though as people have been pointing out with other options, one can trick grep into printing out all lines and highlight the text of interest. ack doesn't need to be tricked to do that - its one of the standard options. And thank you for the clarifying edit. – user450 Dec 7 '14 at 0:31
5

You could use the grep -C flag which gives n lines of context, e.g. grep -C 3 will print the 3 lines before and after the match. There are also -B and -A for before and after.

If you are looking to highlight given strings regularly, e.g. specific log formats it might be worth using python pygmentize with a custom lexer, since it is regex based you will be amazed how easy it is. This latter also has the advantage of being cross platform although some terminals don't do colour very well.

4

I've written a little script that will color whatever string you give it:

#!/usr/bin/env perl
use Getopt::Std;
use strict;
use Term::ANSIColor; 

my %opts;
getopts('hic:l:',\%opts);
    if ($opts{h}){
      print<<EoF; 
Use -l to specify the pattern(s) to highlight. To specify more than one 
pattern use commas. 

-l : A Perl regular expression to be colored. Multiple expressions can be
     passed as comma separated values: -l foo,bar,baz
-i : makes the search case sensitive
-c : comma separated list of colors;

EoF
      exit(0);
    }

my $case_sensitive=$opts{i}||undef;
my @color=('bold red','bold blue', 'bold yellow', 'bold green', 
           'bold magenta', 'bold cyan', 'yellow on_magenta', 
           'bright_white on_red', 'bright_yellow on_red', 'white on_black');
if ($opts{c}) {
   @color=split(/,/,$opts{c});
}
my @patterns;
if($opts{l}){
     @patterns=split(/,/,$opts{l});
}
else{
    $patterns[0]='\*';
}

# Setting $| to non-zero forces a flush right away and after 
# every write or print on the currently selected output channel. 
$|=1;

while (my $line=<>) 
{ 
    for (my $c=0; $c<=$#patterns; $c++){
    if($case_sensitive){
        if($line=~/$patterns[$c]/){
           $line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ge;
        }
    }
    else{
        if($line=~/$patterns[$c]/i){
          $line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ige;
        }
      }
    }
    print STDOUT $line;
}

If you save it as color in a directory that is in your $PATH and make it executable (chmod +x /usr/bin/color), you can color the matched pattern like this:

echo -e "foo\nbar\nbaz\nbib" | color -l foo,bib 

That will produce:

  enter image description here

As written, the script has predefined colors for 10 different patterns, so giving it a comma separated list as I have in the example above will color each of the patterns matched in a different color.

4

I'm a fan of hhighlighter by Paolo Antinori. https://github.com/paoloantinori/hhighlighter

A plus side to this command is that it can highlight up to 10 words with unique colors. Simply pipe the output of a command to h with the words to highlight.

E.g. tail -f /var/log/somelog.log | h "ERROR" will produce:

demo-1


Some examples from his site:

demo-2 demo-3

3

I wrote a program to do this some time ago. I call it cgrep (for color grep).

You can download it by copying the code section from here into an empty file: http://wiki.tcl.tk/38096

Then make the file executable and copy it to one of your regular bin directories.

It's written in tcl so you need tcl installed (8.5 and above). But most linux distros would have tcl installed anyway since lots of software use it (gitk, kernel config, expect etc.).

The syntax for the coloring is simple: regex option option ... You can have as many regex as you like. Here's an example that would color errors in red and warnings in yellow:

tail -f logfile | cgrep '^.*WARNING.*$' -fg yellow '^.*ERROR.*$' -fg red -bg yellow
3

The easiest way looks like this, I think:

tail -f logfile.log | grep -e 'error' -e '**'

enter image description here

No need to install anything.

2

Well, I'm running Fedora 21 and if I type

grep -E \|kk rs.c

it will output the entire contents of the file "rs.c" while highlighting any occurrences of "kk".

  • That's basically the same as arielCo's answer. – Izzy Dec 5 '14 at 20:26
  • 1
    @Izzy: The syntax is shorter though, so I would say it qualifies as a different valid answer. – Nicolas Raoul Mar 16 '16 at 5:58
  • @NicolasRaoul that's why I only left a comment back then (but didn't flag). Basically, I was hoping for the poster to expand a little ;) – Izzy Mar 16 '16 at 7:46
1

A simple trick is to also match an empty string or the beginning of a line; either results in a zero length match for all lines:

grep --color -e 'REGEXP' -e ''
grep --color -e 'REGEXP' -e ^

Or (extended regexp syntax):

grep --color -E 'REGEXP|'
egrep --color 'REGEXP|'
  • 2
    Wouldn't the first variant filter the output for REGEX as well? I just tried, and it does. So that doesn't match the requirements (only highlight, not filter). But the second variant indeed does what the OP requested (verified ;). – Izzy Dec 5 '14 at 18:46
  • @Izzy:sorry, I had a typo. Thanks! – arielCo Dec 5 '14 at 22:28
  • Yeah, that makes sense now :) Filter for REGEX (highlights that term) and for "nothing" (which is "everywhere"). May I suggest you include this little explanation (too make clear what it does), and then we delete our comments (for cleanup)? Thanks! Meanwhile +1 from me :) – Izzy Dec 5 '14 at 22:51
  • Will do, but Fedorqui gave essentially the same answer above (I guess I didn't see it when I posted mine). – arielCo Dec 6 '14 at 2:30
  • Have to admit I didn't see that either... (hint: there is no "above" or "below", as the order of answers depends on what filter you have set ;) – Izzy Dec 6 '14 at 13:14
1

You can use this command

grep --color --context=1000

Or shorter

grep --col -1000

explainshell.com - grep --color --context

1

Use less. The search string found by / is a regular expression, and the occurrences will be highlighted.

1

In my .bashrc I have this function. I call it cgrep, but I'm giving it a slightly more apt name here.

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

I find this useful for tailing logs for example, where I want to highlight a keyword but see everything going on.

tail -f /var/log/SOMELOG | highlight KEYWORD
  • Due credit: I got the "^|" idea from @fedorqui's selected answer. Thanks :) – simesy May 22 '18 at 23:33
0

You can just pipe your output to:

sed "s/\([Ee][Rr][Rr][Oo][Rr]\)/`tput rev`\1`tput rmso`/"

Here I'm using a regexp that will match "error", "ERROR", "ErRoR", etc. in all 32 possible variations.

0

I have the following function defined in my ~/.zshrc:

hl () {
    sed s/$1/$'\e[1;31m'\&$'\e[0;m'/
}

Use it with tail -f logfile.log | hl "error". It adds an escape sequence for Light Red before the highlighted word, and reset to no color after the word. You can find other color codes here: http://tldp.org/HOWTO/Bash-Prompt-HOWTO/x329.html

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

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