Convince grep to output all lines, not just those with matches
Say I have the following file:
$ cat test
test line 1
test line 2
line without the search word
another line without it
test line 3 with two test words
test line 4
By default, grep
returns each line that contains the search term:
$ grep test test
test line 1
test line 2
test line 3 with two test words
test line 4
Passing the --color
parameter to grep
will make it highlight the portion of the line that matches the search expression, but it still only returns lines that contain the expression. Is there a way to get grep
to output every line in the source file, but highlight the matches?
My current terrible hack to accomplish this (at least on files that don’t have 10000+ consecutive lines with no matches) is:
$ grep -B 9999 -A 9999 test test
If grep
can’t accomplish this, is there another command-line tool that offers the same functionality? I’ve fiddled with ack
, but it doesn’t seem to have an option for it either.
grep --color -E "test|$" yourfile
What we’re doing here is matching against the $
pattern and the test pattern, obviously $
doesn’t have anything to colourize so only the test pattern gets color. The -E
just turns on extended regex matching.
You can create a function out of it easily like this:
highlight () { grep --color -E "$1|$" "${@:1}" ; }
You can try:
perl -MTERM::ANSIColor -nle '/pattern/ ? print colored($_, 'color') : print' test
Not very portable however, and even if Perl is installed, you may need to download another module. In addition it will color the entire line, not just the search word.
ack --passthru --color string file
for Ubuntu and Debian, use ack-grep instead of ack
ack-grep --passthru --color string file
I have the following function that I use for such things:
highlight () {
perl -pe "s/$1/e[1;31;43m$&e[0m/g"
}
Internally it looks kind of ugly, it’s nice and easy to use, like so:
cat some_file.txt | highlight some_word
or, for a slightly more real-world example:
tail -f console.log | highlight ERROR
You can change the colors to anything you like (which might be hard with grep–I’m not sure) by changing the 1
and 31
and 43
(after e[
) to different values. The codes to use are all over the place, but here’s a quick intro: the 1 bolds the text, the 31
makes it red, and the 43
gives a yellow background. 32
or 33
would be different colors, and 44
or 45
would be different backgrounds: you get the idea. You can even make it blink (with a 5
) if you’re so inclined.
This doesn’t use any special Perl modules, and Perl is nearly ubiquitous, so I would expect it to work just about anywhere. The grep solution is very clever, but the –color switch on grep is not available everywhere. For instance, I just tried this solution on a Solaris box running bash, and another running ksh, and my local Mac OS X machine running zsh. All worked just fine. Solaris choked on the grep --color
solution, however.
Also, ack is awesome, and I recommend it to anyone who hasn’t yet discovered it, but I’ve had some issues installing it on a few of the many servers I work on. (I forget why: I think related to Perl modules it required.)
Since I don’t think I’ve ever worked on a Unix box that didn’t have Perl installed (with the exception of embedded-type systems, Linksys routers, and such) I’d say this is pretty much a universally useable solution.
There’s a much easier way to do this for GNU grep but I don’t think it’s portable (i.e., BSD grep):
In a pipe:
cat <file> | grep --color=always -z <query>
On a file:
grep --color=always -z <query> <file>
Credit goes to Cyrus’s answer here.
A sed
version,
works on both bash
and ash
.
#highlight
hsed(){
local pattern="$1"
shift
local r=`echo -e 'e'[31m`
local c=`echo -e 'e'[0m`
sed "s:${pattern//:/:}:$r $c:g" "$@"
}
Another way to do this properly and portably with grep
(besides using two regexes with alternation as in the accepted answer) is via the null pattern (and respectively null string).
It should work equally well with both -E
and -F
switches since, per the standard:
-E
Match using extended regular expressions.
[...] A null ERE shall match every line.
and
-F
Match using fixed strings.
[...] A null string shall match every line.
So it’s simply a matter of running
grep -E -e '' -e 'pattern' infile
and respectively
grep -F -e '' -e 'string' infile
OP asked for grep
, and that is what I RECOMMEND; but after trying hard to solve a problem with sed
, for the record, here is a simple solution with it:
sed $'s/main/E[31m&E[0m/g' testt.c
or
cat testt.c | sed $'s/main/E[31m&E[0m/g'
Will paint main
in red.
E[31m
: red color start sequenceE[0m
: finished color mark&
: the matched pattern/g
: all words in a line, not just the first$'string'
is bash strings with escaped characters interpreted
Regarding grep, it also works using ^
(begin of line) instead of $
(end of line). Example:
egrep "^|main" testt.c
And just to show this crazy alias that I DO NOT RECOMMEND, you can even let the open quotes:
alias h='egrep -e"^|'
h main" testt.c
cat testt.c | h main"
all work! 🙂 Don’t worry if you forget to close the quote, bash will remember you with a “continuing line character”.
None of the answers given so far do provide a portable solution.
Here is a portable1 shell function I already posted in a closed as duplicate question that doesn’t require non standard tools or non standard extensions provided with perl
, ack
, ggrep
, gsed
, bash
and the likes but only needs a POSIX shell and the POSIX mandatory utilities sed
and printf
:
grepc()
{
pattern=$1
shift
esc=$(printf " 33")
sed 's"'"$pattern"'"'$esc'[32m&'$esc'[0m"g' "$@"
}
You can use it that way:
grepc string_to_search [file ...]
The highlight color can be adjusted by using one of these codes in the sed command argument (32m being green here):
30m black
31m red
33m yellow
34m blue
35m magenta
36m cyan
37m white
7m reverse video
1 As long as your terminal supports ANSI colors escape sequences.
Instead of using Grep, you can use Less:
less file
Search like this: /pattern
Enter
This will:
- scroll to first matching line
- output every line starting from there on
- highlight all matches
To scroll to next matching line: n
To scroll to previous matching line: N
To toggle highlighting: Esc u
Also you can change the highlighting color if you like.
ripgrep
Use ripgrep
with its --passthru
parameter:
rg --passthru pattern file.txt
It’s one of the fastest grepping tools, since it’s built on top of Rust’s regex engine which uses finite automata, SIMD and aggressive literal optimizations to make searching very fast.
--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 usingrg "^|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.
If you’re interested in the return value of grep
, then GNU sed
is also the way:
sed '/pattern/h; ${p;x;/./Q0;Q1}'
You can add to the script modifications to the line, e.g.:
sed '/pattern/h; ${p;x;/./Q0;Q1}; s/pattern/<