Sign in
← Terminal: getting oriented
3 min · easy · Lesson 5 of 5

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

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

What you'll need for the tasks

Practice in your browser

Linux runs right here. Solve the task and we'll save your progress automatically.

01

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.

{ i }

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.

Get access for $49

Tell the agent: use MCP izi, give me lesson terminal-basics-05-grep-log