Skip to content
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).
  • -A/-B/-C print lines of context around each hit.

It reads a pipe as happily as a file (ps aux | grep nginx). 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. For example:

  • 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
  • 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