Grep Regex: Regular Expressions Syntax and Examples

By 

Updated on

16 min read

Grep regular expressions

Searching a log file for a fixed word is easy, but the questions that come up in practice rarely look like that: find every line that starts with a timestamp, pull out all the IP addresses, or match “error” only when a colon follows it. Regular expressions (regex) turn grep from a word finder into a pattern engine that answers those questions in a single command.

This guide covers regular expression syntax in the GNU version of grep, which is available by default on most Linux distributions, from anchors and character classes through back-references and PCRE lookarounds. If the pattern language itself is new to you, our guide to regular expressions explained covers the fundamentals as they apply across grep, sed, and awk.

Grep Regular Expression Syntax

A regular expression consists of literal characters and meta-characters that have special meaning. GNU grep supports three regular expression syntaxes:

  • Basic Regular Expressions (BRE): the default. Meta-characters ?, +, {, |, (, and ) are treated as literal characters. To use their special meaning, escape them with a backslash (\).
  • Extended Regular Expressions (ERE): enabled with -E (or --extended-regexp). The same meta-characters work without escaping.
  • Perl-Compatible Regular Expressions (PCRE): enabled with -P (or --perl-regexp). Supports advanced features such as lookaheads, lookbehinds, and non-greedy quantifiers.

In GNU grep, BRE and ERE are functionally identical. The only difference is whether meta-characters need to be escaped. PCRE adds features that neither BRE nor ERE support.

You should always enclose the regular expression in single quotes to prevent the shell from interpreting the meta-characters:

Terminal
grep 'pattern' file.txt

Literal Matches

The most basic usage of grep is to search for a literal character or series of characters in a file. For example, to display all lines containing the string “bash” in the /etc/passwd file:

Terminal
grep bash /etc/passwd
output
root:x:0:0:root:/root:/bin/bash
linuxize:x:1000:1000:linuxize:/home/linuxize:/bin/bash

In this example, “bash” is a basic regular expression consisting of four literal characters. The grep command searches for a “b” immediately followed by “a”, “s”, and “h”.

By default, grep is case-sensitive. Use the -i option to ignore case. Note that grep searches for patterns, not whole words, so searching for “gnu” will also match “cygnus” and “magnum”. Use the -w option to match whole words only.

If the search string includes spaces, enclose it in single or double quotation marks:

Terminal
grep "Gnome Display Manager" /etc/passwd

Anchoring

Anchors are meta-characters that allow you to specify where in the line the match must be found.

The ^ (caret) symbol matches the beginning of a line. In the following example, the string “linux” matches only if it occurs at the very beginning of a line:

Terminal
grep '^linux' file.txt

The $ (dollar) symbol matches the end of a line. The following matches lines that end with “linux”:

Terminal
grep 'linux$' file.txt

You can combine both anchors. For example, to find lines that contain only the word “linux”:

Terminal
grep '^linux$' file.txt

The ^$ pattern matches all empty lines, which is useful for filtering them out with grep -v '^$'.

Matching Single Characters

The . (period) symbol is a meta-character that matches any single character. For example, to match anything that begins with “kan”, then has two characters, and ends with “roo”:

Terminal
grep 'kan..roo' file.txt

This would match “kangaroo”, “kaneiroo”, or any other two-character combination between “kan” and “roo”.

Bracket Expressions

Bracket expressions allow you to match any single character from a group by enclosing the characters in brackets []. For example, to find lines that contain “accept” or “accent”:

Terminal
grep 'acce[np]t' file.txt

If the first character inside the brackets is the caret ^, it matches any single character not in the set. The following pattern matches strings like “coca” or “coma”, but not “cola”:

Terminal
grep 'co[^l]a' file.txt

Character Ranges

Instead of listing characters one by one, you can specify a range separated by a hyphen. In the C locale, [a-e] is equivalent to [abcde] and [1-3] is equivalent to [123]. Range behavior can vary in other locales, so use an explicit list when you need to match an exact set of characters.

The following expression matches each line that starts with an uppercase letter in the current locale:

Terminal
grep '^[[:upper:]]' file.txt

POSIX Character Classes

grep supports predefined character classes enclosed in [: and :]. These must be used inside brackets, so the full syntax is [[:class:]]:

ClassDescription
[:alnum:]Alphanumeric characters.
[:alpha:]Alphabetic characters.
[:blank:]Space and tab.
[:digit:]Digits.
[:lower:]Lowercase letters.
[:upper:]Uppercase letters.
[:space:]Whitespace characters.
[:punct:]Punctuation characters.

For example, to match lines that start with a digit:

Terminal
grep '^[[:digit:]]' file.txt

To match lines containing only alphabetic characters:

Terminal
grep '^[[:alpha:]]*$' file.txt

For a complete list of all character classes, see the Grep manual .

Quantifiers

Quantifiers specify how many times the preceding item must occur for a match. The following table shows the quantifiers supported by GNU grep:

QuantifierDescription
*Match the preceding item zero or more times.
?Match the preceding item zero or one time.
+Match the preceding item one or more times.
{n}Match the preceding item exactly n times.
{n,}Match the preceding item at least n times.
{,m}Match the preceding item at most m times.
{n,m}Match the preceding item from n to m times.

The {,m} form is a GNU extension. POSIX spells the same interval {0,m}, which is the safer choice in scripts that have to run on other implementations.

The * (asterisk) character matches the preceding item zero or more times. The following will match “right”, “sright”, “ssright”, and so on:

Terminal
grep 's*right' file.txt

The .* combination matches any number of any characters. The following matches all lines that start with a capital letter and end with a period or comma:

Terminal
grep -E '^[A-Z].*[.,]$' file.txt

The ? (question mark) character makes the preceding item optional, matching it zero or one time. The following matches both “bright” and “right”. When using basic regular expressions, the ? must be escaped:

Terminal
grep 'b\?right' file.txt

Here is the same regex using extended regular expressions:

Terminal
grep -E 'b?right' file.txt

The + (plus) character matches the preceding item one or more times. The following will match “sright” and “ssright”, but not “right”:

Terminal
grep -E 's+right' file.txt

The brace characters {} allow you to specify the exact number or a range of occurrences. The following matches all integers that have between 3 and 9 digits:

Terminal
grep -E '[[:digit:]]{3,9}' file.txt

Alternation

The alternation operator | (pipe) allows you to specify different possible matches, functioning as a logical “OR”. It has the lowest precedence of all regular expression operators.

In the following example, we search for all occurrences of the words “fatal”, “error”, and “critical” in the Nginx log error file:

Terminal
grep 'fatal\|error\|critical' /var/log/nginx/error.log

If you use extended regular expressions with -E, the | operator does not need to be escaped:

Terminal
grep -E 'fatal|error|critical' /var/log/nginx/error.log

Grouping

Grouping allows you to combine patterns together and reference them as a single item. Groups are created using parentheses ().

When using basic regular expressions, the parentheses must be escaped with a backslash (\).

The following example matches both “fearless” and “less”. The ? quantifier makes the (fear) group optional:

Terminal
grep -E '(fear)?less' file.txt

Groups are also useful with alternation. The following matches “cat” or “car”:

Terminal
grep -E 'ca(t|r)' file.txt

Back-references

A group does more than bundle a pattern together: it remembers the text it matched, and \1 through \9 refer back to that text later in the same expression. The numbering counts opening parentheses from the left, so \1 is the first group, \2 the second, and so on.

The most direct use is finding a character that repeats immediately. The following pattern captures any single lowercase letter, then requires the very same letter right after it:

Terminal
printf 'balloon\nbalon\n' | grep '\([a-z]\)\1'
output
balloon

Only “balloon” prints, because it contains “ll” and “oo”. In “balon” no letter is followed by a copy of itself, so \1 never has anything to match.

The same idea catches duplicated words, a typo that is easy to miss in prose and configuration files. Here the group captures a whole word using the \< and \> boundaries, and \1 requires that word to appear again after a space:

Terminal
printf 'the the end\nthe end\n' | grep '\(\<[a-z]\+\>\) \1'
output
the the end

The word boundaries are doing real work in that pattern. Drop them and write '\([a-z]\+\) \1' instead, and the second line matches too, because grep is free to capture just the “e” at the end of “the” and find another “e” at the start of “end”.

GNU grep supports back-references in extended regular expressions too. With -E, the parentheses do not need backslashes:

Terminal
printf 'balloon\nbalon\n' | grep -E '([a-z])\1'
output
balloon

Other grep implementations may not define back-references in extended syntax. Keep the basic form when you need the pattern to be portable.

Escaping Special Characters

To match a meta-character literally, you must escape it with a backslash (\). This is a common source of confusion when searching for characters like ., *, [, ?, or $ in text.

For example, to search for a literal dot:

Terminal
grep '192\.168\.1\.1' file.txt

Without the backslashes, the . would match any character, so “192.168.1.1” would also match “192x168y1z1”.

To search for a literal asterisk:

Terminal
grep '\*' file.txt

Alternatively, you can use the -F option (or --fixed-strings) to treat the entire pattern as a literal string, disabling all regex interpretation:

Terminal
grep -F '192.168.1.1' file.txt

Special Backslash Expressions

GNU grep includes several meta-characters that consist of a backslash followed by a regular character:

ExpressionDescription
\bMatch a word boundary.
\BMatch a position that is not a word boundary.
\<Match the beginning of a word.
\>Match the end of a word.
\wMatch a word constituent, a synonym for [_[:alnum:]].
\WMatch a non-word constituent, a synonym for [^_[:alnum:]].
\sMatch whitespace, a synonym for [[:space:]].
\SMatch non-whitespace, a synonym for [^[:space:]].

Note that \w is defined in terms of [:alnum:] rather than a fixed ASCII range, so in a UTF-8 locale it also matches accented letters and other alphanumeric characters outside a-z, A-Z, and 0-9.

The \b expression is useful for matching whole words without the -w option. The following pattern matches the separate words “abject” and “object” but will not match them if embedded in larger words:

Terminal
grep '\b[ao]bject\b' file.txt

You can use the same boundary pattern with other character sets. For example, to match lines containing a separate three-digit number:

Terminal
grep '\b[0-9]\{3\}\b' file.txt

Matching Digits and Why \d Does Not Work

Perl-style shorthands such as \d are common enough in other languages that reaching for one in grep is close to reflex, and it is the single most frequent source of surprise with grep patterns. Basic and extended regular expressions have no \d. The GNU manual states that the behavior of a backslash followed by a character that is not a special expression is unspecified. GNU grep 3.8 introduced a warning for this pattern:

Terminal
printf 'error 404\nerror ddd\n' | grep '\d'

GNU grep 3.12 prints grep: warning: stray \ before d and currently treats the pattern as the plain letter “d”, so the line that matches is “error ddd”. Do not rely on that fallback because the syntax is unspecified and a future release may reject it or give it a different meaning.

Use a bracket expression instead. The following extended regular expressions use + to match one or more digits:

Terminal
printf 'error 404\nerror abc\n' | grep -E '[0-9]+'
output
error 404
Terminal
printf 'error 404\nerror abc\n' | grep -E '[[:digit:]]+'
output
error 404

The bracket expressions themselves work in both basic and extended syntax. In a basic regular expression, escape the quantifier as [0-9]\+ or [[:digit:]]\+.

In basic and extended syntax, [[:digit:]] is the portable POSIX spelling for the ten ASCII digits. [0-9] matches the same digits in the C locale, but bracket-range behavior can vary in other locales. Under -P, GNU grep treats [[:digit:]] as Unicode decimal digits, while \d and [0-9] match only ASCII digits.

If you would rather keep the Perl spelling, -P gives you the real thing:

Terminal
printf 'error 404\nerror abc\n' | grep -P '\d+'
output
error 404

The same rule covers \D, \h, and the rest of the PCRE shorthands: they exist under -P and nowhere else. The shorthands GNU grep does understand without -P are the ones in the table above.

Perl-Compatible Regular Expressions

The -P option enables Perl-compatible regular expressions (PCRE), which support advanced features not available in BRE or ERE.

Lookaheads match a pattern only if it is followed by (or not followed by) another pattern, without including the second pattern in the match. For example, to match “error” only if it is followed by a colon:

Terminal
grep -P 'error(?=:)' file.txt

To match “error” only if it is not followed by a colon (negative lookahead):

Terminal
grep -P 'error(?!:)' file.txt

Lookbehinds work in the opposite direction. To match a number only if it is preceded by a dollar sign:

Terminal
grep -P '(?<=\$)[0-9]+' file.txt

Non-greedy quantifiers match as few characters as possible. Add ? after a quantifier to make it non-greedy:

Terminal
grep -P '<.*?>' file.txt

This matches the shortest possible string between < and >, rather than the longest.

Info
The -P option is not available in all grep implementations. It is supported by GNU grep on most Linux distributions.

Practical Regex Examples

Below are a few real-world patterns you can copy and adapt. Each example uses the extended syntax (-E) and the -o option to print only the matching portion of each line, which is convenient when you want to extract values rather than full lines.

To extract IPv4 addresses from a log file:

Terminal
grep -E -o '([0-9]{1,3}\.){3}[0-9]{1,3}' /var/log/syslog

This pattern matches any four groups of one to three digits separated by dots. It does not strictly validate octets above 255, but it is enough for most log-scraping tasks.

To extract email addresses from a text file:

Terminal
grep -E -o '\b[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}\b' file.txt

The \b word boundaries prevent the pattern from matching email-like substrings embedded inside larger tokens.

To extract hex color codes (such as #ffcc00) from a CSS file:

Terminal
grep -E -o '#[0-9A-Fa-f]{6}\b' styles.css

The pattern matches a # followed by exactly six hexadecimal characters.

To extract ISO-style dates (such as 2026-05-17) from any file:

Terminal
grep -E -o '[0-9]{4}-[0-9]{2}-[0-9]{2}' file.txt

Use -h if you are searching multiple files and want only the dates without the filename prefix.

To extract URLs that start with http or https:

Terminal
grep -E -o 'https?://[^[:space:]]+' file.txt

The pattern stops at the first whitespace character, which works well for plain text files and most log formats.

Troubleshooting

A back-reference matches text you did not expect
\1 refers to whatever the first group captured, and a loose group captures more than intended. A pattern such as '\([a-z]\+\) \1' can capture a single letter rather than a word, so pin the group down with \< and \> or with an anchor. GNU grep supports back-references under -E, but other implementations may not, so use basic syntax in portable scripts.

The + or ? quantifier matches nothing
Without -E, grep reads basic regular expressions, where + and ? are ordinary characters. Either escape them as \+ and \?, or add -E and write the pattern in extended syntax.

The shell rewrites the pattern before grep sees it
An unquoted pattern containing *, ?, or [ may be expanded as a filename pattern before grep runs. If nothing matches, Bash usually passes the pattern unchanged, while Zsh reports an error by default. A $ can also start parameter or command substitution. Wrap the regular expression in single quotes so the shell passes it to grep unchanged.

grep rejects the -P option
The error reads grep: invalid option -- P, which means the grep on the system is not GNU grep. This is the usual result on macOS and the BSDs, whose grep has no PCRE mode. Install GNU grep (brew install grep provides it as ggrep) or rewrite the pattern in extended syntax.

A pattern with \( or \{ fails to compile
grep reports unbalanced parentheses or braces when the escaping does not match the dialect. In basic regular expressions both the opening and closing characters need a backslash, as in \(ab\)\{2\}; in extended syntax neither does, as in (ab){2}.

Quick Reference

For a printable quick reference, see the Grep cheatsheet .

PatternDescription
.Any single character
^Start of line
$End of line
[abc]Any character in set
[^abc]Any character not in set
[a-z]Any character in range
*Zero or more of preceding
+One or more of preceding (ERE)
?Zero or one of preceding (ERE)
{n}Exactly n of preceding (ERE)
{n,m}Between n and m of preceding (ERE)
[[:digit:]]One digit, the portable stand-in for \d
\bWord boundary
\BNot a word boundary
\<Start of word
\>End of word
\wWord constituent, [_[:alnum:]]
\sWhitespace, [[:space:]]
\|Alternation / OR (BRE)
\(\)Grouping (BRE)
\1Text matched by the first group
\Escape next character

FAQ

What is the difference between basic and extended regular expressions?
The two syntaxes provide the same pattern-matching functionality in GNU grep. In basic regular expressions (BRE), the meta-characters ?, +, {, |, (, and ) must be escaped with a backslash to use their special meaning. In extended regular expressions (ERE, enabled with -E), these characters are special by default. GNU grep supports back-references in both syntaxes, but ERE back-references are not portable to other implementations.

How do I match a literal dot or other special character?
Escape the character with a backslash. For example, use \. to match a literal dot, \* for a literal asterisk, and \[ for a literal bracket. Alternatively, use grep -F to disable regex entirely and treat the pattern as a fixed string.

What does the -P option do?
The -P option enables Perl-compatible regular expressions (PCRE), which support advanced features such as lookaheads ((?=...)), lookbehinds ((?<=...)), and non-greedy quantifiers (*?, +?). These features are not available in basic or extended regular expressions.

Why does \d not work in grep?
\d is a Perl-compatible shorthand, and basic and extended regular expressions do not define it. GNU grep 3.8 introduced the warning grep: warning: stray \ before d, but the resulting behavior is unspecified and may change. Use [0-9] or [[:digit:]] instead, or run the pattern under grep -P '\d'.

How do I match the beginning or end of a word?
Use \b for a word boundary, \< for the beginning of a word, or \> for the end of a word. For example, grep '\bword\b' file.txt matches “word” as a whole word. You can also use grep -w "word" for the same effect.

How do I match whitespace with grep?
Use the \s shorthand to match whitespace such as spaces and tabs in GNU grep, or the POSIX class [[:space:]] for portability. To match one or more whitespace characters, use grep -E '\s+' file.txt or grep '[[:space:]]\+' file.txt. To find blank or whitespace-only lines, use grep -E '^[[:space:]]*$' file.txt.

Conclusion

Regular expressions are used in text editors, programming languages, and command-line tools such as grep , sed , and awk . Knowing how to construct regular expressions is essential for searching text files, writing scripts, and filtering command output.

Linuxize Weekly Newsletter

A quick weekly roundup of new tutorials, news, and tips.

About the authors

Dejan Panovski

Dejan Panovski

Dejan Panovski is the founder of Linuxize, an RHCSA-certified Linux system administrator and DevOps engineer based in Skopje, Macedonia. Author of 800+ Linux tutorials with 20+ years of experience turning complex Linux tasks into clear, reliable guides.

View author page