How to Grep

πŸ—“οΈ
β€’
πŸ”„
β€’
⏳ 5 min

Grep helps you locate any given pattern(s) within one or more files. Very useful when parsing logs!

Keep in mind

Not all Grep implementations are created equal. This post references the GNU version, although hopefully most of the information is generic enough to be of use in most find implementations.

Basics

At the most basic level, Grep commands have the following structure:

grep 'this_string' that_file.md

This will output the full line(s) where this_string was found, highlighting the match itself.

Throw a -n in there to show the line Numbers as well.

Of course one could also grep all files in a given directory:

grep -n 'this_string' .

Would look for this_string in all files in the directory where the command was launched but not in its child directories. Speaking of which…

Not so Basics

Recurse

Say we want to grep recursively all child directories.

grep -rs 'this_string' .

Tells Grep to search Recursively in cwd. The output will be Silent, as in all warnings and errors messages will get Suppressed. This is usually done to avoid useless errors when grepping directories (which will happen with -r).

Multiple searches

Just like Sed, you can use -e to concatenate multiple searches in the same Grep command. Unlike Sed, this does not equate to multiple Grep commands piped together.

Rather, a Grep command with multiple -e expressions will return any pattern that matches any of the expressions, while piping one command into another will only output whatever matches all expressions.

Suppose a grepme file like so:

3183_22_4: option: '3183_22', question: '3183', suffix: '4'
3183_22_5: option: '3183_22', question: '3183', suffix: '5'
3283_23_1: option: '3183_23', '3183', suffix: '1'
3183_23_2: '3183_23', question: '3183', suffix: '2'

grep -e 'option' -e 'question' grepme would output the whole file while grep 'option' grepme | grep 'question' would only output:

3183_22_4: option: '3183_22', question: '3183', suffix: '4'
3183_22_5: option: '3183_22', question: '3183', suffix: '5'

To be clear: -e matches any of the expressions while piping Grep commands together matches all the expressions.

Regex

Again, just like Sed and Find, Grep uses reduced regex by default and the -E flag allows you to use its full regex engine.

If instead you want to avoid regex altogether and look for a literal string with strange characters, use -F.

grep -F '[Hh]ello moto*' file will literally match ”*[Hh]ello moto*”, not β€œHello moto”, not β€œhello moto”, and not β€œ[Hh]ello moto, how you doing?*”.

Common use cases

The classics

sh
grep -rsinv 'foo' .
grep -rsl 'bar' .

The first command will output all lines plus lines numbers (-n) NOT matching foo (-v). It will look for the match recursively (-rs) in a cases Insensitive (-i) fashion. The second one will output all files containing a match (-l, -L would output only files NOT containing a match) for bar, recursively (-rs).

Count

More often than not you’ll need the number of matching lines, more so than the lines themselves. You might be tempted to pipe Grep into wc -l, but there’s no need to!

grep 'hi there!' file | wc -l and grep -c 'hi there!' file produce the same output: They both Count the number of matching lines.

Instead, use this pipe with the -o flag to get the number of Ocurrances (which will differ from -c if there is more than one match per line).

So following the example above:

grep -c '3183_22' grepme ➑️ 2

grep -o '3183_22' grepme | wc -l ➑️ 4

Whole

While -w will match for whole words, -x will match for whole lines:

So for grep -w 'opt' grepme:

βœ… blahblah opt blah
❌ blahblah option blah

While for grep -x 'opt' grepme:

βœ… opt
❌ blahblah opt blah

Fancy things you can do

Context

You might find it useful to have some Context around your Grep results. Use something like -C2 to tell Grep to also print the two lines before and after each match.

Keep in mind that the amount of context lines printed will be limited by other matches as well as the beginning and end of the file.

It’s quite helpful to use this in combination with the -n flag, since Grep uses : to separate the matching lines from their line number and - for context lines.

So given a grepme file like so:

noise1
3183_22_4: option: '3183_22', question: '3183', suffix: '4'
noise2
3183_22_5: option: '3183_22', question: '3183', suffix: '5'
noise3
noise31
3283_23_1: option: '3183_23', '3183', suffix: '1'
noise4
3183_23_2: '3183_23', question: '3183', suffix: '2'
noise5

grep -nC2 '3183_22' grepme will output

1-noise1
2:3183_22_4: option: '3183_22', question: '3183', suffix: '4'
3-noise2
4:3183_22_5: option: '3183_22', question: '3183', suffix: '5'
5-noise3
6-noise31

Exclude and include

You can exclude and include files from the search by a given pattern. Even more fancy, you can use both flags together to fine tune where you are searching exactly.

grep --exclude=*.py --include=main.py 'anexpression' *

Will look in all non *.py files (except for main.py) in cwd for anexpression.

Grep based on a file

Say you have a list of blacklisted words you want to ensure are not present in a project.

grep -f unwantedWords.md projectFile will print out all matches for any of the lines in blacklist.file, while passing it the -l flag from before will print only the problematic filenames.

For this example to make sense, we assume that unwantedWords.md contains one expression or word per line.

Another neat use case: find . | grep -f wantedFiles.md Will output all filenames in cwd listed in the file wantedFiles.md.


Other posts you might like