Skip to content
DevOps devops linux 6 min read

Searching Text with grep

When you run servers, you spend a huge amount of time looking for one line inside thousands of lines of text. Maybe an error in a log file, a setting in a config file, or a process in a list. grep is the tool that finds it. The name stands for “global regular expression print” (a search command that prints matching lines), and it is one of the most-used commands in a DevOps engineer’s day. Once you are comfortable with grep, scanning a 50,000-line log feels as fast as reading one sentence.

What grep does

grep reads text line by line and prints only the lines that match a pattern you give it. The text can come from a file, many files, or from the output of another command piped into it. A “pattern” can be a simple word like error, or a “regular expression” (regex) — a small language for describing text shapes, like “a line that starts with a number” or “an IP address”.

The basic shape of the command is:

grep "pattern" filename

For example, to find every line containing the word denied in the authentication log on Ubuntu:

grep "denied" /var/log/auth.log

Output:

Jun 15 09:14:02 web01 sshd[2841]: Permission denied, please try again.
Jun 15 09:14:08 web01 sshd[2841]: Permission denied, please try again.

Ubuntu 22.04 and 24.04 LTS store most system logs under /var/log. Many of these files (like auth.log and syslog) need sudo to read, so prefix the command with sudo if you see “Permission denied” from grep itself.

The flags you will use every day

A few options turn plain grep into a precision tool. These are worth memorising.

FlagWhat it doesWhen to use it
-iIgnore case (treat Error, ERROR, error the same)When you are not sure how a word is capitalised
-rRecursive — search every file in a folder and its subfoldersSearching a whole config directory like /etc/nginx
-nShow the line number of each matchWhen you need to jump straight to the line in an editor
-vInvert — show lines that do NOT matchFiltering out noise you do not care about
-cCount matching lines instead of printing themQuick “how many times did this happen?” checks
-wMatch whole words onlyAvoid matching error inside errored or mirror
-lList only the file names that contain a match”Which file has this setting?” across many files

Case-insensitive search with -i

Logs and config files are inconsistent about capitals. Use -i so you do not miss anything:

grep -i "error" /var/log/syslog

This matches Error, ERROR, and error all at once.

Recursive search with -r

To find which Nginx config file contains a server_name line, search the whole directory:

grep -rn "server_name" /etc/nginx/sites-available

Output:

/etc/nginx/sites-available/devcraftly.conf:8:    server_name devcraftly.com www.devcraftly.com;
/etc/nginx/sites-available/api.conf:6:    server_name api.devcraftly.com;

Here we combined -r (recursive) and -n (line numbers). Combining flags like this is normal and encouraged.

Line numbers with -n

When grep tells you devcraftly.conf:8, you know the match is on line 8. You can open it straight there in nano with nano +8 /etc/nginx/sites-available/devcraftly.conf or in vim with vim +8 <file>.

Inverting the match with -v

Sometimes it is easier to hide the lines you do not want. To see a config file without blank lines and without comment lines (lines starting with #):

grep -v "^#" /etc/nginx/nginx.conf | grep -v "^$"

The first grep -v "^#" drops comment lines; the second drops empty lines (^$ means “start then immediately end”). This is a clean way to see only the settings that are actually active.

Piping output into grep

grep becomes really powerful when you feed it the output of another command using a pipe (the | symbol, which sends one command’s output into the next). This lets you filter any command’s output.

Find a running process by name:

ps aux | grep nginx

Output:

root      812  0.0  0.1  55180  1740 ?  Ss  09:02  0:00 nginx: master process
www-data  813  0.0  0.3  55620  6204 ?  S   09:02  0:00 nginx: worker process

Check whether a port is listening:

sudo ss -tlnp | grep ":443"

Filter the last lines of a live log as they arrive:

sudo tail -f /var/log/nginx/access.log | grep " 500 "

The tail -f part follows the file in real time, and grep shows you only requests that returned HTTP status 500 (a server error). This is a classic way to watch for problems while you reproduce a bug.

Searching logs for errors

This is the bread-and-butter use of grep. A typical investigation looks like this. First, count how many errors happened today:

sudo grep -ic "error" /var/log/syslog

Output:

27

Then look at the actual lines with context. The -A, -B, and -C flags show lines After, Before, or around (Context) each match:

sudo grep -i -C 2 "error" /var/log/syslog

-C 2 prints the 2 lines before and after every match, which is often where the real cause is.

For systemd services (the system that starts and supervises background programs on Ubuntu), you usually search the journal instead of a file:

sudo journalctl -u nginx --since today | grep -i "fail"

A gentle introduction to regex

A regular expression describes a text pattern rather than exact words. You do not need all of it, but a handful of symbols cover most daily needs:

PatternMeaningExample match
^Start of a line^Jun matches lines starting with Jun
$End of a linefailed$ matches lines ending in failed
.Any single characterb.t matches bat, bit, bot
[0-9]Any one digit[0-9][0-9] matches 42
*Zero or more of the previous itemab* matches a, ab, abbb
|OR (with -E)warn|error matches either word

Basic grep understands the simple symbols above. For OR, grouping, and +, use “extended regex” with the -E flag (or the command egrep, which is the same thing):

grep -E "warn|error|critical" /var/log/syslog

Find lines that look like an IP address:

grep -E "[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+" /var/log/nginx/access.log

Here [0-9]+ means “one or more digits” and \. means a literal dot (the backslash escapes the dot so it is not treated as “any character”).

Watch out: an unquoted pattern with special characters like * or $ can be changed by the shell before grep ever sees it. Always wrap your pattern in double quotes, like grep "error$" file, to be safe.

Best practices

  • Always quote your pattern (grep "pattern" file) so the shell does not mangle special characters.
  • Reach for -i by default when searching logs — capitalisation is rarely consistent.
  • Combine -rn to find both the file and the exact line in one shot across a directory.
  • Use -C 2 (or -A/-B) on errors to capture the surrounding lines that explain the cause.
  • Prefer grep -E for OR and grouping instead of fragile escaping.
  • Use -v to strip out known noise, and -c for a fast count before diving into details.
  • Remember sudo for protected files under /var/log — the “Permission denied” may come from reading the file, not from your pattern.
Last updated June 15, 2026
Was this helpful?