Bash and Linux

Show every ERROR line with its line number

easygrep and regex Must-do

Problem statement

An on-call alert fired and you need to see what the API server logged. Print every line whose level is ERROR (uppercase), prefixed with its line number so you can jump to it in an editor. The lowercase error on the last line is free text, not a log level, and must not be printed.

app.log

TEXT
2026-09-20 10:00:01 INFO Starting api server on :8080
2026-09-20 10:00:02 INFO Connected to database
2026-09-20 10:00:15 WARN Slow query took 1200ms
2026-09-20 10:01:03 ERROR Failed to reach payments service
2026-09-20 10:01:04 INFO Retrying request
2026-09-20 10:01:09 ERROR Payments service timeout after 5s
2026-09-20 10:02:00 INFO Health check passed
2026-09-20 10:02:30 INFO error budget report sent

Examples

Example 1

Input: grep -n 'ERROR' app.log

Output: 4:2026-09-20 10:01:03 ERROR Failed to reach payments service 6:2026-09-20 10:01:09 ERROR Payments service timeout after 5s

Explanation: grep is case-sensitive by default, so line 8 is skipped.

Example 2

Input: grep -c 'ERROR' app.log

Output: 2

Explanation: -c prints only the number of matching lines.

Hints

Approach

Optimal

grep prints every line that contains the pattern. -n adds the 1-based line number and a colon in front of each match. Quoting the pattern in single quotes stops the shell from interpreting any special characters. Because grep matches case-sensitively by default, the lowercase error in the INFO message on line 8 is not printed, which is what we want. If you wanted to be stricter and match ERROR only as a whole word, add -w.

Bash
grep -n 'ERROR' app.log

Follow-up questions

  • How would you follow the log live and only print new ERROR lines? (tail -f app.log | grep --line-buffered ERROR)
  • How would you search every rotated log, including .gz files? (zgrep)

Frequently asked questions

-i makes the match case-insensitive, so it would also print line 8, where error is part of a normal INFO message. Match the exact casing your log format uses for levels. If levels can vary in case, anchor on the position instead, e.g. grep -nE '^[^ ]+ [^ ]+ ERROR '.

Use context flags: -B 2 prints 2 lines before each match, -A 2 after, and -C 2 both. grep separates non-adjacent groups with --.