Regular expressions (regex) describe text patterns. On a server, they’re how you find the interesting lines in huge logs, extract IP addresses, validate input, and edit config files with one command. The core syntax is small. Once you know a dozen symbols, you can read most patterns.
Not to be confused with shell wildcards (*.log). See wildcards and globbing.
The essentials
| Pattern | Meaning | Example match |
|---|---|---|
abc |
Literal text | abc |
. |
Any single character | a.c → abc, a-c |
^ |
Start of line | ^Error |
$ |
End of line | \.conf$ |
[abc] |
One of these characters | gr[ae]y |
[^abc] |
Any character except these | [^0-9] |
[a-z0-9] |
Ranges | [0-9] |
* |
Previous item 0+ times | ab*c → ac, abbbc |
+ |
1+ times (ERE) | [0-9]+ |
? |
0 or 1 time (ERE) | colou?r |
{n,m} |
Between n and m times (ERE) | [0-9]{1,3} |
( ) |
Group (ERE) | (GET|POST) |
| |
Alternation: either side (ERE) | error|fail |
\ |
Escape a special character | \. matches a dot |
Character classes: [[:digit:]], [[:alpha:]], [[:space:]], [[:alnum:]]. In PCRE: \d (digit), \w (word character), \s (whitespace), \b (word boundary).
BRE, ERE and PCRE
Linux tools speak slightly different dialects:
| Dialect | Used by | Note |
|---|---|---|
| BRE (basic) | grep, sed by default |
+ ? | ( ) { } need a backslash: \+, \(, \{ |
| ERE (extended) | grep -E, sed -E, awk |
+ ? | ( ) { } work directly |
| PCRE | grep -P, Perl, Python, PHP, most languages |
Adds \d, \w, lookarounds, non-greedy *? |
Tip: use grep -E and sed -E to avoid backslash soup.
grep examples
grep -E 'error|fail|critical' /var/log/syslog
grep -Ei 'timeout' app.log # case-insensitive
grep -Ec '" 5[0-9]{2} ' access.log # count 5xx responses
grep -Ev '^\s*(#|$)' /etc/ssh/sshd_config # config without comments and blank lines
grep -Eo '([0-9]{1,3}\.){3}[0-9]{1,3}' auth.log | sort | uniq -c | sort -rn | head # IPs
grep -P 'user=\w+' app.log
sed examples
sed -E 's/^#?PermitRootLogin .*/PermitRootLogin no/' -i /etc/ssh/sshd_config
sed -E 's/listen [0-9]+;/listen 8080;/' nginx.conf
sed -E 's/(\w+)@example\.com/\[email protected]/g' users.txt # \1 = first group
sed -n -E '/^\[mysqld\]/,/^\[/p' /etc/mysql/my.cnf # print a section
Test without -i first. Check the output, then apply. See sed command examples.
awk examples
awk '$9 ~ /^5/ {print $7}' access.log | sort | uniq -c | sort -rn | head # URLs returning 5xx
awk '/Failed password/ {print $(NF-3)}' /var/log/auth.log | sort | uniq -c | sort -rn
See awk command examples.
Useful patterns for server work
| Purpose | Pattern (ERE) |
|---|---|
| IPv4 address (rough) | ([0-9]{1,3}\.){3}[0-9]{1,3} |
| HTTP status in an Nginx log | " [0-9]{3} |
| ISO date | [0-9]{4}-[0-9]{2}-[0-9]{2} |
| Email (rough) | [A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,} |
| Blank or comment line | ^\s*(#|$) |
| Trailing whitespace | [[:space:]]+$ |
“Rough” patterns are fine for searching logs. Validating input (emails, IPs) properly needs stricter rules, or library functions.
Common pitfalls
- Unescaped dots:
example.comalso matchesexampleXcom. Useexample\.com. - Greedy matching:
".*"matches from the first quote to the last one on the line. Use"[^"]*"instead (or.*?in PCRE). - Shell quoting: wrap regexes in single quotes, so the shell doesn’t interpret
$,*or\. - Dialect mix-ups:
\ddoesn’t work ingrep -E. Use[0-9], orgrep -P.
Quick reference
| Need | Syntax |
|---|---|
| Start / end of line | ^ / $ |
| Any character | . |
| Repeat | * + ? {n,m} |
| Alternatives | a|b (ERE) |
| Group and reuse | ( ) and \1 |
| Extended mode | grep -E, sed -E |
Related: find and rename files and pipes and redirection.