84

In bash, if I want to execute a command and only display output lines that matches a certain pattern, I can pipe it to grep, like

file testfile

hello
there
my
friends

command

$ cat testfile | grep 'hello'
hello #this will be highlightd

this will highlight the search match and display the entire line it falls on. I can use -A and -B to display lines before and after that line. My question is is it possible to execute the command and display all output as normal, but to highlight the search matches like grep would? so my ouput would be

hello #highlighted
there
my
friends
Flimzy
  • 4,374
  • 21
  • 41
ewok
  • 4,091
  • 15
  • 46
  • 66
  • 6
    See [Convince grep to output all lines, not just those with matches](//unix.stackexchange.com/q/366/80216) (U&L) or [Colorized grep — viewing the entire file with highlighted matches](//stackoverflow.com/q/981601/3960947) (Stack Overflow) for better answers. – G-Man Says 'Reinstate Monica' Feb 06 '17 at 04:39

7 Answers7

85

To use a Color GREP to only highlight matched patterns but not otherwise change the output:

grep --color=always -e "^" -e "hello" testfile

The first pattern will match all lines (all lines will be printed) the second pattern (and any following patterns) cause the matched text to be highlighted in color.

Since the first pattern matches all lines but does not match on a printable character, it does not add any color highlighting so it doesn't compete/interfere with the readability of the highlighted text.

bertieb
  • 7,344
  • 36
  • 42
  • 54
bot779
  • 861
  • 6
  • 2
38

Add option -z to your GNU grep command:

cat testfile | grep --color=always -z 'hello'

or shorter

grep --color=always -z 'hello' testfile
Cyrus
  • 5,441
  • 1
  • 22
  • 30
  • 5
    This is a very crude hack.   It has the effect of treating the entire file as a single line.   Therefore, (1) if the file is very large, there may be a possibility of running out of memory, and (2) if the file doesn’t contain the pattern *at all*, then nothing will be output. – G-Man Says 'Reinstate Monica' Feb 06 '17 at 04:41
  • What version of grep is this supported on? On grep 2.5.4, -z doesn't seem available... – Alex Jun 29 '17 at 22:29
  • @Alex: I used GNU grep version 2.6.3. – Cyrus Jun 30 '17 at 02:37
  • 3
    @G-Man: more resource-saving: `grep --color "hello\|$" file` – Cyrus Feb 09 '18 at 17:06
  • @Cyrus: Yes, that’s a better answer. Unfortunately, it is essentially equivalent to [bot779’s answer](https://superuser.com/q/914856/354511#1192944) to this question, and almost identical to the accepted answers ([1](https://stackoverflow.com/a/981831/3960947#981831) and [2](https://unix.stackexchange.com/q/366/80216#367)) to the questions I linked to. – G-Man Says 'Reinstate Monica' Feb 09 '18 at 18:10
  • @G-Man: Thanks for the hint. I missed that. – Cyrus Feb 09 '18 at 18:15
  • For whatever reason this doesn't work with kubectl in zsh – Michael Cole Jun 03 '19 at 13:26
10

This one works with GNU grep as well as with grep on FreeBSD:

grep --color=always 'hello\|$'

It matches the text "hello" or (\|) the non-printable null string at the end of each line ($). That's why each line gets printed but only "hello" is highlighted.

Chances are you already have --color=auto configured in your shell. Then you most likely don't need to specify --color=always:

grep 'hello\|$'

You can also simpler version by using egrep (with extended regular expressions), where | for the "or" expression doesn't need to be escaped:

egrep 'hello|$'

See also similar question on StackOverflow: Colorized grep -- viewing the entire file with highlighted matches

Marián Černý
  • 326
  • 3
  • 6
  • This doesn’t answer how to display all the input lines. – Chris Page Feb 15 '20 at 03:17
  • @ChrisPage I would say it does: *That's why **each line gets printed** but only "hello" is highlighted.* – Marián Černý Feb 16 '20 at 07:07
  • To be pedantic — _"the non-printable null string at the end of each line"_ — There is _**not**_ a "non-printable null string" (`0x00`) at the end of lines, there is a _line-terminator_ / end-of-line which will generally be `0x0d`, `0x0a`, or `0x0d0x0a` (CR, LF, CRLF) – Stephen P Oct 01 '20 at 00:28
  • 1
    You are confusing a null string with a null character (`\0`). Null string is an empty string. The term null string is taken from the regex documentation. See for example: https://man7.org/linux/man-pages/man7/regex.7.html – Marián Černý Oct 01 '20 at 21:04
8

Similarly to previous answer, you can catch all $ end of lines:

cat testfile | grep --color -E "hello|$"

-E (or --extended-regexp) means that special characters must be escaped with \. When using it, | will be treated as regex "OR" condition.

Grep |$ will also catch and print all lines which has an end, but since $ is a hidden character, it cannot be highlighted.

Update:

If you'd like to print all output, but also return exit code, whether match was found or not, you can use perl command:

cat testfile | \
perl -pe 'BEGIN {$status=1} END {exit $status} $status=0 if /hello/;'

If you prefer sed - Here's an example how to highlight all matches + return exit code if no match found: https://askubuntu.com/a/1200851/670392

Noam Manos
  • 1,804
  • 1
  • 20
  • 20
2

Adding to the top answer above. The highlight{} function mentioned in the comments only works when data is piped into it. The following alias, while not perfect, is more useful:

alias greph="grep --color=always -e^ -e"

This works with commands such as:

greph foo bar.txt

cat bar.txt | greph foo
rpmohn
  • 21
  • 2
1

Here's a solution using ripgrep, a modern, easier to use (IMO), and much faster replacement for grep:

cat my-file | rg --passthru '<REGEX>'

You can also easily customise the "highlighter colour":

# dark red foreground with light yellow background:
cat my-file | rg --passthru --colors match:fg:124 --colors match:bg:229 '<REGEX>'

See man rg (online version).


For everyday use I created a shell alias:

# in .bashrc
alias hl='rg --passthru --colors match:fg:124 --colors match:bg:229'

Now it's as simple as cat my-file | hl '<REGEX>'.

cyqsimon
  • 739
  • 1
  • 9
  • 20
0

-z is good to output the entire file as in the answer from bot779 but most times you need to control how many lines you want to include above and below the match. You can control the length of context with -C <N> where N is the number of lines of context. See the example below:

cat >testfile <<EOM
hello
there
my
friends
more
lines
to demonstrate
context
EOM

cat testfile | grep --color=always -C 2 friends

there
my
*friends* highlighted
more
lines

Using a large number such as -C 1000 can also limit the output if the output you are filtering is too big.

evpo
  • 114
  • 2