Users started reporting failures around lunchtime. The service's log — the file where software writes down everything it does, like a diary — sits at /var/log/api.log, and it is thousands of lines long. Somewhere in there are the lines that explain the problem. Reading the whole thing is not an option; you need the tool that reads it for you.

grep is that tool: it prints only the lines of a file that match a pattern — the word or fragment you are hunting for — and hides everything else. It turns "scroll through forty thousand lines" into "read the twelve that matter".

What grep actually does

The shape is always the same: grep PATTERN FILE — "show me every line of FILE that contains PATTERN". The name is odd but the idea is a needle in a haystack: you describe the needle, and grep hands you every straw that contains one, one line at a time.

grep -i error /var/log/api.log       # matching lines, any capitalisation
grep -n -i error /var/log/api.log    # ...with line numbers
grep -v DEBUG /var/log/api.log       # everything EXCEPT matches
grep -r "TODO" src/                  # search every file in a directory
grep -C 3 -i error /var/log/api.log  # 3 lines of context around each hit

Source: /var/log/api.log (8 of 40,000 lines)

12:41:02 INFO  request GET /orders 200 41ms
12:41:03 DEBUG cache hit orders:1041
12:41:07 ERROR db connect timeout (5000ms)
12:41:07 INFO  request GET /orders 503 5002ms
12:41:09 WARN  retry 1/3 db-01
12:41:14 Error: pool exhausted, dropping request
12:41:14 INFO  request GET /health 200 2ms
12:41:15 DEBUG cache miss orders:1042

Output: grep -n -i error /var/log/api.log

3:12:41:07 ERROR db connect timeout (5000ms)
6:12:41:14 Error: pool exhausted, dropping request

-i matched both ERROR and Error; -n added the line numbers so you can jump straight to them.

Same file, two lines. grep reads every line, keeps the ones that contain the pattern and hides the rest. It never changes the file.

The letters after the dash are flags — switches that adjust how a command behaves. Five of grep's come up constantly: -i ignores capitalisation, -n adds line numbers, -v inverts the match, -r searches a whole directory, and -C 3 shows three lines of context either side of each hit.

FlagWhat it doesExample
-iIgnore capitalisation while matching.grep -i error api.log
-nPrefix each matching line with its line number.grep -n error api.log
-vInvert: print the lines that do not match.grep -v DEBUG api.log
-rRecurse: search every file under a directory.grep -r "TODO" src/
-C 3Show 3 lines of context before and after each hit.grep -C 3 -i error api.log
-cCount matching lines instead of printing them.grep -c -i error api.log
-EUse extended patterns, for example alternatives.grep -E -i "error|timeout" api.log
The flags you will use every week. They combine freely: grep -n -i -C 2 error api.log is line numbers, any capitalisation, two lines of context.

The wrong tools people reach for first

Three commands look tempting here, and each teaches the same lesson about picking a tool by its job. cat prints the whole file — all forty thousand lines, and now you are spotting the bad ones by eye. tail shows the end of the file: recent lines, but all of them, and the evidence you need may be from two hours ago. find searches for files by name — a different question entirely; you already know which file, you need what is inside it. grep is the only one whose whole job is "matching lines, nothing else".

The flag that saves the hunt: -i

Here is the trap that catches almost everyone once: software is inconsistent about capital letters. The same log can contain ERROR, Error and error, written by different parts of the system. Without -i, grep matches exactly what you typed and silently misses the rest — half the evidence gone, and nothing tells you it is missing. When you hunt keywords in logs, -i is not optional politeness. It is the habit.

Where you'll meet this on the job

Almost all operational evidence is text — logs, configuration files, code — which is why grep is the most-used investigation command in the shell. It gets stronger when you chain it to other tools with the pipe (|), which feeds one command's output into the next:

tail -f api.log | grep -i error    # live errors only, as they happen
grep -i error api.log | wc -l      # how many? count the matches
ps aux | grep nginx                # find a running process

That first line is what an engineer's screen looks like during an incident: the log streaming live, filtered to the lines that matter. Small tools, composed — that is the shell's whole philosophy, and grep sits in the middle of it.

One detail worth knowing before it bites: when grep's output goes to your screen it prints each match immediately, but when it goes into another pipe it saves output in a buffer and sends it in blocks. So tail -f api.log | grep -i error | cut -d' ' -f1 can look silent for a while. GNU grep has --line-buffered for exactly this case; add it whenever grep sits in the middle of a live pipeline.

After this, you can: pull the handful of lines that matter out of a log with thousands — any capitalisation, with line numbers and context, counted or watched live.