datarekha

Searching with grep

Learn to search file contents with grep — the fastest way to find which files contain a pattern, on what line, and in what context.

8 min read Beginner Command Line Lesson 5 of 14

What you'll learn

  • Run grep to find lines matching a pattern in one file or an entire directory tree
  • Use essential flags: -i, -r, -n, -w, -v, -c, -l, -o, and -A/-B/-C
  • Pass grep a piped stream and write basic regex patterns with -E

Before you start

The last lesson ended on a wish: instead of scrolling four million lines, just ask the file to show you the ones that match ERROR. That command exists, and it is one of the most-typed tools in any engineer’s day. It is grep, and it turns “read the whole file” into “show me only the lines I care about” — across one file or four hundred.

What grep does

grep reads lines of text and prints only the ones that match a pattern — a string or a regular expression you supply. Think of it as a filter: every line goes in, only matching lines come out.

input lines ──► [ grep PATTERN ] ──► matching lines
all lines(input)grep PATTERN(filter)matching lines(output)
grep passes only lines that match your pattern

Basic syntax

grep PATTERN file

Search config.py for the string API_KEY:

grep API_KEY config.py
API_KEY = "abc123"

You can search multiple files at once by listing them or using a glob:

grep API_KEY config.py settings.py
grep API_KEY *.py

Essential flags

-n — show line numbers

Tells you exactly where the match lives. Almost always useful.

grep -n API_KEY config.py
14:API_KEY = "abc123"

-i — case-insensitive

Matches api_key, API_KEY, Api_Key, etc.

grep -i api_key config.py

-r / -R — recursive through a directory

This is the answer to the opening question. Search every file under src/:

grep -r API_KEY src/
src/config.py:14:API_KEY = "abc123"
src/utils/auth.py:3:API_KEY = os.environ.get("API_KEY")

-r follows symbolic links on most systems; -R always does on GNU grep.

-l — list filenames only

When you only need to know which files match, not the lines themselves:

grep -rl API_KEY src/
src/config.py
src/utils/auth.py

-c — count matching lines per file

grep -rc API_KEY src/
src/config.py:1
src/utils/auth.py:1
src/tests/test_auth.py:0

-w — whole word

Without -w, grep error matches errors, errored, and fatal_error. With -w it only matches the standalone word error.

grep -w error app.log

-v — invert (lines that do NOT match)

Invert flips the filter: print every line that does not match the pattern. Handy for stripping comments or blank lines.

grep -v "^#" config.ini

That ^# is a tiny regex — ^ anchors to the start of the line, so this removes lines that begin with #.

-o — print only the matching part

Instead of the whole line, emit just the text that matched:

grep -o "API_KEY" config.py
API_KEY

More useful combined with regex (shown below).

-A, -B, -C — context lines

Real errors don’t exist in isolation. Context flags print surrounding lines so you can see what caused a match.

FlagMeaning
-A NN lines After the match
-B NN lines Before the match
-C NN lines before and after (Context)
grep -C 2 "TypeError" app.log
Traceback (most recent call last):
  File "app.py", line 42, in process
    result = int(value)
TypeError: int() argument must be a string
During handling of the above exception:

Reading from a pipe

grep works seamlessly in a pipeline. The command to the left produces lines; grep filters them.

ps aux | grep nginx
root      1234  0.0  nginx: master process
www-data  1235  0.1  nginx: worker process
cat app.log | grep -i "error"

You can chain multiple greps to narrow results further:

grep -r "def " src/ | grep -v "test_"

This finds all function definitions except those in test files.

A gentle intro to regex in grep

grep patterns are regular expressions — a mini-language for describing text shapes. You don’t need to learn all of it now. Here are the most useful pieces:

PatternMatches
.any single character
^foolines that start with foo
foo$lines that end with foo
[aeiou]any one vowel
[0-9]any digit

To use extended regex (which adds +, ?, \d, \w, and more), pass -E:

grep -E "[0-9]{4}" data.txt
port: 8080
year: 2026

[0-9]{4} matches exactly four consecutive digits.

grep -E "^(ERROR|WARN)" app.log

Matches lines that begin with either ERROR or WARN.

Putting it all together

Back to the opening question — 400 files, find every mention of API_KEY with line numbers:

grep -rn API_KEY .
./src/config.py:14:API_KEY = "abc123"
./src/utils/auth.py:3:API_KEY = os.environ.get("API_KEY")
./docs/setup.md:22:export API_KEY=your_key_here

Three files, three lines, under a second. That’s grep.

In one breath

grep PATTERN file is a filter: every line goes in, only the matching ones come out. A handful of flags do almost everything — -n adds line numbers, -i ignores case, -r recurses through a whole directory, -l lists just the filenames, -c counts, -w matches whole words, -v inverts (lines that do NOT match), and -A/-B/-C print lines of context around each hit. It reads a pipe as happily as a file (ps aux | grep nginx), and its patterns are regular expressions^ start, $ end, [0-9] a digit, with -E for the extended set. One rule keeps you safe: wrap patterns in single quotes so the shell does not eat the *, $, or ( before grep ever sees them.

Practice

Before the quiz, build three real searches: (a) find every line in app.log that contains error in any case, with line numbers; (b) from config.ini, print only the lines that are not blank and not comments starting with #; (c) recursively under src/, list just the filenames that mention TODO. Write each command — and say why the pattern in (b) needs single quotes.

Quick check

0/3
Q1Which flag prints the line number alongside each match?
Q2You want every line in error.log that does NOT contain the word 'DEBUG'. Which command is correct?
Q3A teammate asks: 'I need to search every .js file under src/ for the string fetch( and see two lines of context after each match — what single command does that?' Which answer is correct?

A question to carry forward

grep is unbeatable at one specific question: which lines, inside files, contain this text? But step back and notice what it quietly assumes — that you already know which files to point it at. Half the time the real question is about the files themselves, not their contents: every .log larger than 100 MB, everything modified in the last hour, all the stray .pyc files you want to delete across a deep tree. grep cannot ask any of those — it reads what is inside files, not which files exist. The tool that walks the directory tree and matches files by their name, size, age, and type is find, and it is the next lesson.

Sign in to track your progress

Completed lessons, your XP, level, and streak save to your account — it's free and takes a few seconds.

Related lessons

Skip to content