Searching Text with grep

Linux · Lesson 12 of 32 6 min read Updated

grep searches text for lines that match a pattern and prints them. It is probably the command you will use most after ls and cd, because so much of Linux is text: logs, configuration files, CSV data and source code.

In this lesson you will search a web server log and an SSH login log for useful facts, learn the options that matter, and write simple regular expressions (patterns that describe text) to find things like IP addresses and error codes.

The form is grep PATTERN FILE.... grep prints every line that contains the pattern. Priya's server keeps SSH login messages in auth.log:

Terminal
grep "Failed password" auth.log
Output
2026-10-30T06:12:01.102341+05:30 shop-server sshd[1201]: Failed password for root from 192.0.2.55 port 51122 ssh2
2026-10-30T06:12:04.551203+05:30 shop-server sshd[1201]: Failed password for root from 192.0.2.55 port 51122 ssh2
2026-10-30T06:13:40.008812+05:30 shop-server sshd[1230]: Failed password for invalid user admin from 198.51.100.89 port 40210 ssh2
2026-10-30T08:44:09.661270+05:30 shop-server sshd[1411]: Failed password for invalid user test from 198.51.100.89 port 40388 ssh2

Put the pattern in quotes whenever it contains spaces or special characters, otherwise the shell splits it into separate arguments. Single quotes are safest, because the shell changes nothing inside them.

The options you will use every day

OptionMeaning
-iIgnore upper/lower case
-nShow line numbers
-cCount matching lines instead of printing them
-vInvert: show lines that do not match
-wMatch whole words only
-oPrint only the matching part, not the whole line
-rSearch every file in a directory tree
-lList only the names of files that match
-A n, -B n, -C nShow n lines after, before, or around each match
-FTreat the pattern as plain text, not a regular expression
Terminal
grep -c "Failed password" auth.log
grep -v " 200 " access.log
grep -w "Pen" orders.csv
Output
4
192.0.2.55 - - [29/Oct/2026:10:03:02 +0530] "GET /wp-login.php HTTP/1.1" 404 162 "-" "curl/8.5.0"
192.0.2.55 - - [29/Oct/2026:10:03:03 +0530] "GET /.env HTTP/1.1" 404 162 "-" "curl/8.5.0"
198.51.100.23 - - [29/Oct/2026:10:05:30 +0530] "POST /order HTTP/1.1" 201 87 "-" "Mozilla/5.0"
192.0.2.55 - - [29/Oct/2026:10:06:45 +0530] "GET /admin HTTP/1.1" 404 162 "-" "curl/8.5.0"
203.0.113.7 - - [29/Oct/2026:10:09:20 +0530] "GET /order HTTP/1.1" 500 310 "-" "Mozilla/5.0"
1002,Priya,Jaipur,Pen Set,250
1007,Farhan,Pune,Pen Set,250

There were four failed logins. -v " 200 " hides every successful request, leaving only the interesting ones: one address probing for /wp-login.php, /.env and /admin (typical bot scanning) and one server error (500). -w "Pen" matches the word "Pen" but would not match "Pencil" or "Open".

Searching many files

-r searches a whole directory tree and prefixes each match with the file name. -l prints only the file names, which is perfect for "which config file sets this?":

Terminal
grep -r "listen" site-config
grep -rl "8080" site-config
grep -rn "server_name" /etc/nginx/sites-available
Output
site-config/blog.conf:listen 8080;
site-config/shop.conf:listen 80;
site-config/blog.conf
/etc/nginx/sites-available/shop:4:    server_name shop.example.in;

Combine -r with -i and -n and you have a fast search tool for any project. Add --include='*.conf' to search only matching file names.

Context around a match

A log line rarely tells the whole story. -A 1 (after) shows the next line too, -B shows lines before, and -C both:

Terminal
grep -A 1 "Accepted" auth.log
Output
2026-10-30T07:02:15.774410+05:30 shop-server sshd[1302]: Accepted publickey for priya from 203.0.113.7 port 55012 ssh2: ED25519 SHA256:q3Xr9mJb0eL2vKcR8sTnYpWz4uHaD1fG6iOkB7xVyZw
2026-10-30T07:02:15.790021+05:30 shop-server sshd[1302]: pam_unix(sshd:session): session opened for user priya(uid=1000) by priya(uid=0)

Regular expressions

A regular expression (regex) is a pattern language for text. By default grep uses basic regular expressions; grep -E switches on extended ones, where +, ?, {}, | and () work without backslashes. Use -E and you will rarely be surprised.

PatternMatches
^text"text" at the start of a line
text$"text" at the end of a line
.Any single character
[0-9]One digit; [a-z] one lower-case letter
[^,]Any character except a comma
x* / x+ / x?Zero or more / one or more / zero or one x
x{3} / x{1,3}Exactly three / one to three x
cat|dog"cat" or "dog" (needs -E)
\.A real dot, not "any character"

Two practical examples: find every request that ended in a 4xx or 5xx error, then pull out every IP address in the SSH log and count them.

Terminal
grep -E '" (4|5)[0-9]{2} ' access.log | cut -d' ' -f1,7,9
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' auth.log | sort | uniq -c | sort -rn
Output
192.0.2.55 /wp-login.php 404
192.0.2.55 /.env 404
192.0.2.55 /admin 404
203.0.113.7 /order 500
      2 198.51.100.89
      2 192.0.2.55
      1 203.0.113.7

The first pattern means: a closing quote, a space, a 4 or a 5, exactly two more digits, then a space. That is exactly where the status code sits in a web log line. The second matches four groups of 1 to 3 digits separated by dots, and -o prints only that part of each line.

Old and new

You may see egrep and fgrep in older scripts and books. They were short names for grep -E and grep -F. Current GNU grep prints a warning such as "egrep: warning: egrep is obsolescent" when you use them, so write grep -E and grep -F instead. For very large code bases, many developers now use ripgrep (rg, installed with sudo apt install ripgrep), which searches folders recursively by default, skips files listed in .gitignore and is very fast. Plain grep is still the tool that is installed everywhere.

Common mistakes

  • Forgetting quotes. grep Failed password auth.log searches for "Failed" in the files password and auth.log.
  • Using . to mean a dot. grep "192.0.2.5" also matches 192x0y2z5. Escape it as 192\.0\.2\.5, or use -F for plain text.
  • Expecting + or | to work without -E. In basic mode they are ordinary characters.
  • Searching binary files. grep prints "binary file matches" instead of lines. Add -a only if you really need the text.

Key points

  • grep PATTERN FILE prints matching lines; quote the pattern.
  • -i ignores case, -n numbers lines, -c counts, -v inverts, -w matches words, -o prints only the match.
  • -r searches folders, -l lists matching files, -A/-B/-C add context.
  • Use grep -E for extended regular expressions: ^, $, [], +, ?, {n}, |, ().
  • Use grep -E/grep -F, not the obsolete egrep/fgrep.

Exercise

Using access.log in the format shown above, write commands that: count the requests that came from 203.0.113.7; list only the lines for POST requests; show every request whose path ends in .html; and print the line number of every 404 response.

Show solution
Terminal
grep -c '^203\.0\.113\.7 ' access.log
grep '"POST ' access.log
grep -E '\.html HTTP' access.log
grep -n '" 404 ' access.log
Output
4
198.51.100.23 - - [29/Oct/2026:10:05:30 +0530] "POST /order HTTP/1.1" 201 87 "-" "Mozilla/5.0"
198.51.100.23 - - [29/Oct/2026:10:01:15 +0530] "GET /menu.html HTTP/1.1" 200 3301 "-" "Mozilla/5.0"
203.0.113.7 - - [29/Oct/2026:10:06:11 +0530] "GET /contact.html HTTP/1.1" 200 2048 "-" "Mozilla/5.0"
4:192.0.2.55 - - [29/Oct/2026:10:03:02 +0530] "GET /wp-login.php HTTP/1.1" 404 162 "-" "curl/8.5.0"
5:192.0.2.55 - - [29/Oct/2026:10:03:03 +0530] "GET /.env HTTP/1.1" 404 162 "-" "curl/8.5.0"
8:192.0.2.55 - - [29/Oct/2026:10:06:45 +0530] "GET /admin HTTP/1.1" 404 162 "-" "curl/8.5.0"

Anchoring with ^ and escaping the dots makes sure only that exact address at the start of the line counts. Including the quote and spaces around the status code avoids matching 404 inside a byte count or a path.

Quick Quiz

1. Which command counts the lines containing "error", ignoring case?

2. What does grep -v "^#" sshd_config show?

3. Which pattern needs grep -E (or backslashes) to work?

4. Which file names does grep -rl "8080" /etc/nginx print?

Learn Linux with a mentor

Free lessons get you started. Our Ethical Hacking & Cyber Security training adds real projects and code reviews from working developers.

  • Project-based learning
  • Mentor code reviews
  • Free counselling call