Skip to content
elephantoo

grep & regular expressions

Lesson 12 of 31 16 min read

Search text with grep, its most useful options, and basic and extended regular expressions.


grep searches text for lines matching a pattern. It's one of the most-used commands on any Linux machine: digging through logs, finding where a function is defined, checking a config setting, filtering the output of other commands. Paired with regular expressions, it becomes a precision tool.

Sample log (app.log) used throughout:

Output
2026-10-01 09:00:01 INFO  Server started on port 8080
2026-10-01 09:05:12 WARN  Disk usage at 85%
2026-10-01 09:07:45 ERROR Database connection refused
2026-10-01 09:08:00 INFO  Retrying database connection
2026-10-01 09:08:02 error payment-service timeout after 30s
2026-10-01 09:10:30 INFO  User asha@example.com logged in
2026-10-01 09:12:01 ERROR Failed to send mail to ben@example.org

The basics#

Terminal
grep ERROR app.log
Output
2026-10-01 09:07:45 ERROR Database connection refused
2026-10-01 09:12:01 ERROR Failed to send mail to ben@example.org

grep prints every line containing the pattern. It's case-sensitive, so the lowercase error line was skipped. The most useful options:

OptionEffect
-iIgnore case
-vInvert: lines that do not match
-nShow line numbers
-cCount matching lines
-wMatch whole words only
-oPrint only the matching part
-lPrint only names of files with a match
-r / -RSearch directories recursively (-R follows symlinks)
-A 2 / -B 2 / -C 2Also show 2 lines After / Before / around each match
-qQuiet: no output, just the exit status
-FFixed string: treat the pattern as plain text, not a regex
Terminal
grep -i error app.log
Output
2026-10-01 09:07:45 ERROR Database connection refused
2026-10-01 09:08:02 error payment-service timeout after 30s
2026-10-01 09:12:01 ERROR Failed to send mail to ben@example.org
Terminal
grep -c -i error app.log
grep -n WARN app.log
Output
3
2:2026-10-01 09:05:12 WARN  Disk usage at 85%
Terminal
grep -v INFO app.log        # hide the noise
Output
2026-10-01 09:05:12 WARN  Disk usage at 85%
2026-10-01 09:07:45 ERROR Database connection refused
2026-10-01 09:08:02 error payment-service timeout after 30s
2026-10-01 09:12:01 ERROR Failed to send mail to ben@example.org

Context is gold when debugging:

Terminal
grep -A1 "connection refused" app.log
Output
2026-10-01 09:07:45 ERROR Database connection refused
2026-10-01 09:08:00 INFO  Retrying database connection

Searching many files#

Terminal
grep -rn TODO src          # recursive, with file names and line numbers
Output
src/app.py:2:    # TODO: add args
src/notes.txt:1:TODO: write docs
Terminal
grep -rl TODO .                         # just the file names
grep -r --include="*.py" TODO .         # only Python files
grep -r --exclude-dir=node_modules TODO .

grep in a script uses its exit status: 0 if something matched, 1 if not.

Terminal
grep -q ERROR app.log && echo "errors found"
Output
errors found

And it's the universal filter at the end of a pipe: ps aux | grep nginx, dpkg -l | grep python3, history | grep ssh.

Regular expressions#

A regular expression (regex) is a pattern language. grep understands two dialects: basic (the default) and extended (grep -E). Use -E; it needs fewer backslashes.

PatternMeaningExample matches
.Any single character09:1. → 09:10, 09:12
^ / $Start / end of line^2026, refused$
[abc]One of these charactersgr[ae]y → gray, grey
[^abc]Any character except these[^0-9] → a non-digit
[0-9], [a-z]Ranges
[[:digit:]], [[:alpha:]], [[:space:]]Character classes (locale-safe)
*Previous item 0 or more timesab*c → ac, abbbc
+1 or more times (-E)[0-9]+ → 85, 8080
?0 or 1 time (-E)colou?r → color, colour
{n}, {n,m}Exactly n / between n and m times (-E)[0-9]{4} → 2026
a|ba OR b (-E)ERROR|WARN
( )Group (-E)(ab)+ → abab
\bWord boundary (GNU)\bport\b
\Escape a special character\. is a literal dot

Regex * is not the shell's *. In a regex, * means "repeat the previous thing"; "anything" is .*.

Practical patterns

Terminal
grep -E "ERROR|WARN" app.log | wc -l           # errors or warnings
Output
3
Terminal
grep -E "^2026-10-01 09:0[0-5]" app.log       # lines from 09:00 to 09:05
Output
2026-10-01 09:00:01 INFO  Server started on port 8080
2026-10-01 09:05:12 WARN  Disk usage at 85%
Terminal
grep -oE "[0-9]+%" app.log                     # pull out percentages
Output
85%
Terminal
grep -oE "[[:alnum:]._%+-]+@[[:alnum:].-]+\.[a-z]{2,}" app.log   # extract email addresses
Output
asha@example.com
ben@example.org

The email regex reads: one or more allowed characters, an @, a domain, a literal dot (\.) and at least two letters. (Real-world email validation is harder; this is good enough for searching logs.)

Some more you'll reuse:

Terminal
grep -vE "^\s*(#|$)" /etc/ssh/sshd_config     # config without comments and blank lines
grep -E "^[0-9]{1,3}(\.[0-9]{1,3}){3}" access.log   # lines starting with an IPv4 address
grep -oP "(?<=port )\d+" app.log               # Perl regex (-P, GNU): prints 8080

The first one is a sysadmin favourite: it shows only the settings that are actually active.

Literal text with -F

When you search for text that contains regex characters, like 1.2.3 or [error] or $PATH, use -F so nothing is special:

Terminal
grep -F "[error]" nginx.log

Always single-quote patterns that contain $, *, | or \, so the shell leaves them alone.

Beyond grep: ripgrep#

For searching large codebases, ripgrep (rg) is a popular faster alternative: recursive by default, respects .gitignore and skips binary files. Install it with sudo apt install ripgrep (Fedora: sudo dnf install ripgrep) and use it like rg TODO or rg -i "connection refused" /var/log.

Common mistakes#

  • Forgetting -E and wondering why +, ? or | don't work.
  • Using the shell's * meaning inside a regex (grep "*.log").
  • Not escaping the dot: grep "1.5" also matches 105 and 1x5.
  • ps aux | grep nginx also lists the grep process itself. Use pgrep -a nginx, or the trick grep [n]ginx.
  • Searching binary or huge directories without --exclude-dir.

What's next#

grep becomes even more powerful when you chain it with other commands. Next: pipes and redirection, covering stdin, stdout and stderr, |, >, 2>&1, tee and xargs.

Check your understanding

Quick quiz

0/3 answered
  1. 1.Which command lists only the NAMES of files under src/ that contain the word TODO?

  2. 2.In a regular expression, what does ^ERROR match?

  3. 3.Why does grep -E 'ERROR|WARN' app.log work, while grep 'ERROR|WARN' app.log usually finds nothing?

Finished reading?

Mark this lesson complete to track your progress.