RED HAT ENTERPRISE LINUX

grep and Regular
Expressions

Analyzing Text on the Command Line

CIS126RH | RHEL System Administration 1
Mesa Community College

One of the most powerful skills for a Linux administrator is the ability to search and filter text efficiently. grep combined with regular expressions lets you find exactly what you need in log files, configuration files, and command output — without opening a text editor. These skills are tested on the RHCSA exam and used every day in production.

Learning Objectives

1
Use grep to search files and output Apply common options to filter and count matching lines
2
Construct basic regular expressions (BRE) Use anchors, character classes, and quantifiers
3
Write extended regular expressions (ERE) Use alternation, grouping, and ERE quantifiers with grep -E
4
Apply grep in real RHEL admin tasks Filter logs, config files, and command output in pipelines

What is grep?

grepGlobal Regular Expression Print

Reads lines of text and prints any line that matches a pattern. Works on files or standard input piped from another command.

Syntax

grep [OPTIONS] PATTERN [FILE...]

💡 Exit Codes

0 = at least one match found
1 = no match
2 = error (bad option, file not found)
Exit codes make grep scriptable.

# Search /etc/passwd for the word 'root'
[student@rhel ~]$ grep root /etc/passwd
root:x:0:0:root:/root:/bin/bash

# Case-insensitive search
[student@rhel ~]$ grep -i ROOT /etc/passwd
root:x:0:0:root:/root:/bin/bash

# Search standard input from a pipe
[student@rhel ~]$ ps aux | grep httpd

Common grep Options

Option Long form Effect Example
-i--ignore-caseCase-insensitive matchgrep -i error syslog
-v--invert-matchShow non-matching linesgrep -v '^#' httpd.conf
-n--line-numberPrefix each match with its line numbergrep -n failed auth.log
-c--countPrint count of matching lines onlygrep -c FAILED secure
-l--files-with-matchesList filenames that contain a matchgrep -l root /etc/*.conf
-r--recursiveSearch all files under a directorygrep -r sshd /etc/
-E--extended-regexpEnable extended regular expressionsgrep -E 'cat|dog' file
-o--only-matchingPrint only the matched portiongrep -oE '[0-9]+' data
-A n--after-context=nShow n lines after each matchgrep -A 3 error syslog
-B n--before-context=nShow n lines before each matchgrep -B 2 error syslog

Practical Examples

Filter config files

# Show only active (non-comment) lines
[student@rhel ~]$ grep -v '^#' \
    /etc/ssh/sshd_config

Count failed login attempts

[student@rhel ~]$ grep -c 'Failed password' \
    /var/log/secure

Find which files reference a user

[student@rhel ~]$ grep -rl 'student' /etc/

Show context around errors

[student@rhel ~]$ grep -i -A 3 \
    'error' /var/log/messages

Filter live command output

# Listening web and SSH ports
[student@rhel ~]$ ss -tlnp | \
    grep -E ':22|:80|:443'

# Running services only
[student@rhel ~]$ systemctl list-units \
    | grep running
💡 Tip

Combine -i and -n to find where errors appear in large log files: grep -in error /var/log/messages

Introduction to Regular Expressions

A regular expression (regex) is a pattern that describes a set of strings. grep uses them to decide which lines match.

Two standards used with grep on RHEL:

  • BRE — Basic Regular Expressions (grep default)
  • ERE — Extended Regular Expressions (grep -E)
⚠ Key Difference

ERE does not require backslashes before +, ?, |, and (). BRE requires \+, \?, \|, and \(\).

MetacharacterMeaning
.Any single character
*Zero or more of preceding
^Anchor: start of line
$Anchor: end of line
[ ]Character class
[^ ]Negated character class
\Escape the next metacharacter

Anchors — ^ and $

Anchors match a position in the line, not a character. They do not consume any text.

PatternMatches lines that…
^rootStart with root
bash$End with bash
^#Are comment lines
^$Are completely blank
^[^#]Are not comment lines
🎯 RHCSA Focus

Filtering config files to show only active settings — removing comment and blank lines — is a very common exam task.

# Lines starting with 'root'
[student@rhel ~]$ grep '^root' /etc/passwd
root:x:0:0:root:/root:/bin/bash

# Lines ending with '/bin/bash'
[student@rhel ~]$ grep '/bin/bash$' /etc/passwd

# Remove comments AND blank lines (BRE)
[student@rhel ~]$ grep -v '^#\|^$' \
    /etc/ssh/sshd_config

# Same with ERE (cleaner syntax)
[student@rhel ~]$ grep -vE '^(#|$)' \
    /etc/ssh/sshd_config

Character Classes [ ]

Literal ranges

PatternMatches
[aeiou]Any single vowel
[A-Z]Any uppercase letter
[0-9]Any digit
[a-zA-Z0-9]Any alphanumeric character
[^0-9]Any non-digit character

POSIX named classes

ClassEquivalent to
[:alpha:][a-zA-Z]
[:digit:][0-9]
[:alnum:]letters and digits
[:space:]whitespace characters
[:upper:]uppercase letters only
# Lines containing any digit
[student@rhel ~]$ grep '[0-9]' /etc/passwd

# Match lowercase username field
[student@rhel ~]$ grep '^[a-z]*:x:1' /etc/passwd

# POSIX class — locale-safe digit match
[student@rhel ~]$ grep '[[:digit:]]' data.txt

# Lines with whitespace (tabs or spaces)
[student@rhel ~]$ grep '[[:space:]]' file.txt
📖 POSIX

POSIX named classes ([[:digit:]]) are locale-independent and safer than simple ranges ([0-9]) on systems with non-ASCII locales.

Quantifiers

Quantifiers follow an element and control how many times it must match.

BRE (default grep) ERE (grep -E) Meaning ERE Example
**Zero or morego*gle → ggle, gogle, google
\++One or morego+gle → gogle, google (not ggle)
\??Zero or one (optional)colou?r → color or colour
\{n\}{n}Exactly n times[0-9]{4} → any 4-digit number
\{n,\}{n,}n or more times[a-z]{3,} → 3+ lowercase letters
\{n,m\}{n,m}Between n and m times[0-9]{1,3} → 1 to 3 digits
# Match lines with two or more consecutive spaces (ERE)
[student@rhel ~]$ grep -E ' {2,}' config.txt

# Match 4-digit port numbers in ss output
[student@rhel ~]$ ss -tlnp | grep -E ':[0-9]{4}'

The Dot . and Escaping \

Dot matches any single character

# 'c', any character, 't'
[student@rhel ~]$ grep 'c.t' words.txt
cat  cut  cot  c3t  c_t

# Exactly 3-character words
[student@rhel ~]$ grep '^...$' words.txt
⚠ Common Mistake

The pattern 192.168.1.1 also matches 192X168Y1Z1 because . is any character. Use 192\.168\.1\.1 to match the literal dots in an IP address.

Escaping makes metacharacters literal

PatternMatches literally
\.A period / dot
\*An asterisk
\[A left bracket
\$A dollar sign
# Literal IP address match (ERE)
[student@rhel ~]$ grep -E \
  '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}' \
  /var/log/secure

ERE: Alternation and Grouping

Alternation with |

Match one or another pattern. Use grep -E (or write \| in BRE).

# Match 'Failed' or 'Rejected' in logs
[student@rhel ~]$ grep -E \
  'Failed|Rejected' /var/log/secure

# Case-insensitive with alternation
[student@rhel ~]$ grep -iE \
  'error|warning|critical' \
  /var/log/messages

Grouping with ( )

Group sub-expressions to apply quantifiers to a whole group.

# 'ha' repeated one or more times
[student@rhel ~]$ grep -E '(ha)+' file.txt
ha haha hahaha

# Optional protocol prefix (ERE)
[student@rhel ~]$ grep -E \
  '(https?://)?example\.com' access.log
⚠ BRE equivalent

In BRE without -E, use \| for alternation and \( \) for grouping.

BRE vs ERE — Quick Reference

Feature BRE — grep (default) ERE — grep -E
One or more\++
Zero or one\??
Alternation\||
Grouping\( \)( )
Interval — exact\{n\}{n}
Interval — range\{n,m\}{n,m}
Zero or more**
Any character..
Line anchors^ $^ $
Character class[abc][abc]
🎯 RHCSA Tip

When in doubt, use grep -E. ERE is a strict superset of BRE — any BRE pattern also works with -E. ERE syntax is cleaner and less error-prone.

grep in Pipelines

grep reads standard input when no file is given, making it ideal for filtering the output of other commands.

Filtering command output

# Firewall services in effect
[student@rhel ~]$ firewall-cmd --list-all \
    | grep services

# Processes owned by the apache user
[student@rhel ~]$ ps aux | grep '^apache'

# Installed packages with 'http' in name
[student@rhel ~]$ rpm -qa | grep -i http

Chaining multiple greps

# Errors but NOT debug-level messages
[student@rhel ~]$ grep -i error /var/log/messages \
    | grep -iv debug

# Top IPs with failed SSH logins
[student@rhel ~]$ grep 'Failed password' \
    /var/log/secure \
  | grep -oE '[0-9]{1,3}(\.[0-9]{1,3}){3}' \
  | sort | uniq -c | sort -rn | head
🎯 Exam Tip

Practice reading multi-stage pipelines aloud. The exam may test comprehension of a given pipeline, not just authoring one from scratch.

Real-World Scenarios

🔐 Security Audit
# Who has sudo rights?
grep -v '^#' /etc/sudoers \
  | grep ALL

Identify privileged accounts without reading every line of the sudoers file.

🌐 Web Server Debug
# 404 errors from the last hour
grep -E ' 404 ' \
  /var/log/httpd/access_log \
  | grep "$(date +%H:)"

Isolate HTTP errors from millions of log entries in seconds.

⚙ Config Review
# Active sshd settings only
grep -vE '^(#|$)' \
  /etc/ssh/sshd_config

See only effective configuration — every comment and blank line stripped.

💡 User Management
# Users with an interactive shell
grep -v '/nologin\|/false' /etc/passwd
💡 Storage Check
# Filesystems over 80% used
df -h | grep -E '[89][0-9]%|100%'

grep Variants

Command Regex type Notes
grepBREDefault — use \+, \?, \|, \( \)
grep -EEREPreferred for complex patterns; cleaner syntax
egrepEREDeprecated alias for grep -E — avoid in scripts
grep -FFixed stringNo regex processing; fastest; safest for literal text
fgrepFixed stringDeprecated alias for grep -F
grep -PPCREPerl-compatible; powerful but not POSIX — avoid on exam
⚠ RHCSA Note

Use grep and grep -E on the exam. The deprecated aliases egrep and fgrep still work on RHEL 9 but should not be used in new scripts. grep -P (PCRE) is not required for RHCSA.

💡 When to use grep -F

Use grep -F when searching for a literal string that contains regex metacharacters — for example, a literal IP address like 192.168.1.1 or a dollar sign. Faster and avoids accidental regex interpretation.

Finding the Right Approach

I know the text I want to find, no special characters:

grep 'pattern' file — plain grep, no flags needed

I want to find lines that do NOT match:

grep -v 'pattern' file — invert match

My pattern needs + ? | or ( ) without backslashes:

grep -E 'pattern' file — extended regex

I am searching for a literal string with dots, asterisks, or brackets:

grep -F 'literal.string' file — fixed string, no regex

I need to search recursively across an entire directory:

grep -r 'pattern' /path/ — recursive search

I want to count matches, not see them:

grep -c 'pattern' file — count only

Knowledge Check

Answer these before moving to the next slide:

  1. What option makes grep search recursively through a directory?
  2. Write a grep command to show only non-comment, non-blank lines from /etc/httpd/conf/httpd.conf.
  3. What is the difference between ^[0-9] and [^0-9]?
  4. Write an ERE pattern to match either eth0 or ens3 in a file.
  5. Why should you use grep -F when searching for the literal text 192.168.1.1?
💡 Study Tip

Test every answer at the terminal before reading the answers slide. Typing the command yourself is the fastest path to remembering it on exam day.

Knowledge Check — Answers

  1. grep -r or grep --recursive searches all files under a directory tree.
  2. grep -vE '^(#|[[:space:]]*$)' /etc/httpd/conf/httpd.conf
  3. ^[0-9] matches lines that start with a digit. [^0-9] matches any single character that is not a digit — the ^ inside brackets negates the class.
  4. grep -E 'eth0|ens3' file
  5. Without -F, the dots in 192.168.1.1 are treated as "any character" metacharacters, so 192X168Y1Z1 would also match. grep -F treats every character literally, eliminating false positives.

Key Takeaways

  • 1
    grep pattern file is the primary text-search tool. Pipe it from any command that produces output. Exit codes (0=match, 1=no match) make it scriptable.
  • 2
    Master the essential options: -i (case), -v (invert), -n (line numbers), -c (count), -r (recursive), -E (extended regex).
  • 3
    Anchors (^ start, $ end), character classes ([0-9], [[:alpha:]]), and quantifiers (*, +, {n,m}) are the core building blocks of every useful pattern.
  • 4
    Use grep -E for alternation (|) and grouping (()) without backslashes. ERE syntax is cleaner and a superset of BRE — default to it for complex patterns.

Graded Lab

  • Use grep -v to display only active (non-comment, non-blank) lines from /etc/ssh/sshd_config
  • Use grep -E with alternation to find lines containing either Failed or Accepted in /var/log/secure
  • Use grep -oE and a quantifier pattern to extract all IPv4 addresses from /var/log/secure
  • Use a pipeline with grep, sort, and uniq -c to count the top five source IPs in failed login attempts
🎯 Exam Practice Files

/etc/passwd  ·  /etc/ssh/sshd_config  ·  /var/log/secure  ·  /var/log/messages  ·  /proc/cpuinfo