Here's a different approach. I have a little Perl script which I have already posted in another answer that will highlight the user provided patterns in different colors. A slightly modified version of the script will act like grep
:
#!/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_blue',
'bright_white on_yellow', '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=<>)
{
my $want=0;
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;
$want++;
}
}
else{
if($line=~/$patterns[$c]/i){
$line=~s/($patterns[$c])/color("$color[$c]").$1.color("reset")/ige;
$want++;
}
}
}
print STDOUT $line if $want>0;
}
If you save that script as cgrep
somewhere in your PATH
and make it executable, you can specify up to 10 different patterns, each of which will be printed in a different color:
$ cgrep -h
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;
I'm guessing that you probably have a file or directory called debug
in your current working directory:
$ ls -l
total 8
-rw-r--r-- 1 jay wheel 58 Feb 1 05:01 T
$ grep [D]ebug T
1 Debug
$ grep [Dd]ebug T
1 Debug
2 debug
$ touch debug
$ ls -l
total 8
-rw-r--r-- 1 jay wheel 58 Feb 1 05:01 T
-rw-r--r-- 1 jay wheel 0 Feb 1 05:05 debug
$ grep [D]ebug T
2 debug
$ grep [Dd]ebug T
2 debug
I commend you on this excellent illustration why you must always escape shell metacharacters.
Update to clarify what's going on: I'm assuming you, like I, are using an OS with a case-insensitive filesystem (e.g. a Mac). When you execute the command, your shell performs a number of expansions on it before actually executing grep
. One of those is filename expansion in which square brackets provide alternations:
[Dd]ebug
on a case-sensitive filesystem would expand to either Debug debug
, Debug
or debug
depending on what files it could match. Since it only matched the file debug
, your command became:
grep debug T
If you remove that file and do touch Debug
, you'll see the output of your command change because it will be interpolated as
grep Debug T
Without the debug
file in your directory, your shell tries to interpolate the brackets but fails without a match so it happens to pass the argument through unchanged.
Best Answer
If you have GNUgrep installed (eg
/usr/bin/ggrep
or/opt/gnu/bin/grep
on Solaris 11,/opt/sfw/bin/ggrep
on Solaris 10) then you have the-m
flag.Instead of
grep
you could usesed