Searching logs with grep
Count how many errors are in the application log and save the number.
A service starts returning errors, and the first thing you do is open its
log. Nobody reads thousands of lines by eye. grep pulls out the lines you
need: it prints the lines of a file that contain a given pattern. The same
tool counts errors, shows what happened around an error, and finds which
request fails more often than the others.
Where to practice
The examples below work in a ubuntu:24.04 container. You can bring up
the same one yourself:
docker run --rm -it ubuntu:24.04 bash
On Linux and in WSL2 the commands work directly in your own terminal too.
On macOS grep is different (the BSD one); the difference is covered in
"Common mistakes".
Create the log we'll try things on:
printf '2026-10-09 09:00:01 INFO start\n2026-10-09 09:00:02 INFO connected to db\n2026-10-09 09:00:05 ERROR timeout\n2026-10-09 09:00:06 INFO retry\n2026-10-09 09:00:07 ERROR auth failed\n2026-10-09 09:00:09 WARN slow query\n2026-10-09 09:00:12 error budget: 2%% used\n2026-10-09 09:00:15 ERROR db down\n' > /tmp/app.log
Each line has a date, a time, a level (INFO, WARN, ERROR), and a
message. %% in printf turns into a single % character.
What grep prints
grep pattern file prints the lines that contain the pattern:
grep ERROR /tmp/app.log
2026-10-09 09:00:05 ERROR timeout
2026-10-09 09:00:07 ERROR auth failed
2026-10-09 09:00:15 ERROR db down
The pattern is matched as a substring anywhere in the line, and
case-sensitively. That's why the error budget line didn't make the cut:
there, error is lowercase.
You can skip the file argument, and then grep reads stdin. That's how
it's put at the end of a pipeline: ps -ef | grep '[s]leep' from the
lesson about processes.
Useful flags
-c(count) prints the number of matching lines instead of the lines themselves.-i(ignore case) searches case-insensitively.-v(invert) prints the lines where the pattern is absent.-n(number) adds the line number.-w(word) matches the pattern only as a whole word:-w dbfindsconnected to db, but notdbx.
grep -c ERROR /tmp/app.log
3
grep -ci error /tmp/app.log
4
With -i, the error budget line is now counted too. That's not an
application error, just a word in a message. So -i tends to get in the
way when counting errors: log levels are usually written in capitals.
grep -n ERROR /tmp/app.log
3:2026-10-09 09:00:05 ERROR timeout
5:2026-10-09 09:00:07 ERROR auth failed
8:2026-10-09 09:00:15 ERROR db down
grep -v INFO /tmp/app.log
2026-10-09 09:00:05 ERROR timeout
2026-10-09 09:00:07 ERROR auth failed
2026-10-09 09:00:09 WARN slow query
2026-10-09 09:00:12 error budget: 2% used
2026-10-09 09:00:15 ERROR db down
Flags combine: grep -vc INFO gives 5.
-c counts lines, not matches. In the line a ERROR b ERROR there are two
occurrences of ERROR, but grep -c counts 1. If you need the actual
matches, -o prints each one on its own line, and wc -l counts them:
grep -o ERROR file | wc -l.
Several patterns at once
To find both errors and warnings, join the patterns with | and the -E
flag (extended regular expressions):
grep -E 'ERROR|WARN' /tmp/app.log
2026-10-09 09:00:05 ERROR timeout
2026-10-09 09:00:07 ERROR auth failed
2026-10-09 09:00:09 WARN slow query
2026-10-09 09:00:15 ERROR db down
The pattern is in single quotes, otherwise the shell would treat | as a
pipe.
A grep pattern is a regular expression, and some characters in it are
special. A dot means "any character": grep 'a.b' finds both a.b and
axb. A ^ at the start of the pattern anchors it to the start of the
line, $ at the end anchors it to the end of the line: grep 'used$'
finds lines that end in used. If you need the pattern as plain text,
without special characters, add -F (fixed strings).
What was near the error
A line with an error is often useless without its neighbors. Three flags
set the context: -A N (after) adds N lines after, -B N (before) N
lines before, -C N adds N lines on both sides.
grep -C1 'auth' /tmp/app.log
2026-10-09 09:00:06 INFO retry
2026-10-09 09:00:07 ERROR auth failed
2026-10-09 09:00:09 WARN slow query
Several files and directories
Logs usually get rotated: the old log is renamed to app.log.1, and new
lines go into a fresh app.log. If you give grep several files, it
prefixes each line with the file name:
cp /tmp/app.log /tmp/app.log.1
grep -c ERROR /tmp/app.log /tmp/app.log.1
/tmp/app.log:3
/tmp/app.log.1:3
With several files, -c counts each one separately and no longer prints a
single number. -h removes the file names, -l prints only the names of
files where the pattern was found. -r (recursive) searches every file in
a directory and its subdirectories: grep -r ERROR /var/log/app. It's
handy for searching, but if you need just today's log, point at that one
file.
Which request fails more often
A web server writes each request as its own line in the request log (access log). Make a small example:
printf '10.0.3.17 - - [09/Oct/2026:14:01:12 +0000] "GET /img/404.png HTTP/1.1" 200 5120\n10.0.3.42 - - [09/Oct/2026:14:01:15 +0000] "GET /api/cart HTTP/1.1" 404 153\n10.0.7.5 - - [09/Oct/2026:14:01:20 +0000] "GET /old/page HTTP/1.1" 404 153\n10.0.3.17 - - [09/Oct/2026:14:01:22 +0000] "GET /api/cart HTTP/1.1" 200 2048\n10.0.3.42 - - [09/Oct/2026:14:01:30 +0000] "GET /api/cart HTTP/1.1" 404 153\n' > /tmp/access.log
Each line has the client's IP, the time, the request in quotes, the
response code, and the response size in bytes. Code 404 means "not
found". Let's count those responses:
grep -c 404 /tmp/access.log
4
There are actually three 404 responses. The fourth line is a request for
the image /img/404.png, which the server answered with 200: the
pattern matched inside the path. grep doesn't know where the response
code is in the line, so the pattern needs to be more precise. The code
comes right after the closing quote and a space:
grep -c '" 404 ' /tmp/access.log
3
Now let's find which path gets 404 most often. The path is the seventh
field of the line, and awk '{print $7}' cuts it out (same as the first
field in the lesson about du). Next, identical lines are grouped together
with sort, and uniq -c folds adjacent repeats into one line with a
count. Without sort before it, uniq only counts repeats that are next
to each other. The final sort -rn puts the most frequent ones on top
(-n by number, -r in reverse order):
grep '" 404 ' /tmp/access.log | awk '{print $7}' | sort | uniq -c | sort -rn
2 /api/cart
1 /old/page
This pipeline works with any field: replace $7 with $1, and you get
which IP sent the most of these responses. | head -1 keeps only the top
line.
Collect several answers into one file
> overwrites the file, >> appends a line to the end. To insert a
command's output into the middle of another line, write the command
inside $( ): the shell runs it first and substitutes the result.
echo "errors=$(grep -c ERROR /tmp/app.log)" > /tmp/report.txt
echo "warnings=$(grep -c WARN /tmp/app.log)" >> /tmp/report.txt
cat /tmp/report.txt
errors=3
warnings=1
If the second line were also written with >, only it would be left in
the file.
Common mistakes
grep -iwhen counting errors: lines where the worderrorsimply shows up in a message get counted too.grep ERRORacross several files or with-rwhen you need one log: lines from rotatedapp.log.1and neighboring files get pulled in, and-cprints a number per file with file names.- A pattern with no anchor to a position in the line:
grep 404matches the response code as well as404in a path or a size. uniq -cwithoutsortbefore it: identical lines that aren't adjacent get counted as several separate groups.- A pattern with
|,*,$, or spaces without quotes: the shell parses it before it reachesgrep. - macOS: it has BSD
grep. The flags-c,-i,-v,-n,-w,-E,-r,-o,-A/-B/-Cwork the same way. There's no-Pflag (Perl regular expressions).wc -lon macOS prints the number with leading spaces, as noted in the lesson aboutwc.
What you'll need for the tasks
- Finding lines by pattern and counting them with
grep -c, without catching extra files or lines with the same word in a different case. - Making a pattern more precise so it matches only the right spot in the line.
- Finding the most frequent value of a field with
awk '{print $N}' | sort | uniq -c | sort -rn. - Collecting several answers into one file with
>,>>, and$( ). - Everything from earlier lessons:
wc -l,du,pgrep, sorting by size, and saving an answer to a file.
Practice in your browser
Linux runs right here. Solve the task and we'll save your progress automatically.
How many errors in the log
/tmp/app.log holds the application's output. Count how many lines contain the word ERROR and save the number to /tmp/answer.
0%
Go through it with an AI agent
The agent explains anything unclear in plain words, gives you 3 labs for this lesson and drills you on interview questions. $49 one-time, lifetime access.
Tell the agent: use MCP izi, give me lesson terminal-basics-05-grep-log
Progress saved in this browser
Clear your history or open this on your phone, and the marks will be gone. Leave your email and they'll move to your account. No password needed.
Done. moved to your account. You can see your progress on any device. My progress