/commands/grep/grep-multiple-patterns
grep multiple patterns: grep -e foo -e bar, grep -E 'foo|bar'
- tool
- grep
- versions
- GNU grep 3.x
- os
- linux, macos, windows
To print lines that match any of several patterns, pass each one with -e (grep -e error -e warning log.txt) or join them with | in an extended regular expression (grep -E 'error|warning' log.txt). Without -E, grep uses basic regular expressions where | is a literal character, so grep 'error|warning' searches for the text error|warning and usually prints nothing.
OR: any of the patterns
printf 'INFO start\nWARNING disk 80%%\nERROR disk full\nINFO done\n' > app.log
echo '--- -e'
grep -e ERROR -e WARNING app.log
echo '--- -E'
grep -E 'ERROR|WARNING' app.log
echo '--- basic regex, unescaped |'
grep 'ERROR|WARNING' app.log || echo "no match (exit $?)"
echo '--- basic regex, escaped \| (GNU extension)'
grep 'ERROR\|WARNING' app.logOutput
--- -e WARNING disk 80% ERROR disk full --- -E WARNING disk 80% ERROR disk full --- basic regex, unescaped | no match (exit 1) --- basic regex, escaped \| (GNU extension) WARNING disk 80% ERROR disk full
Patterns from a file
-f FILE reads one pattern per line. Add -F when the patterns are fixed strings, not regular expressions (faster, and dots or brackets are not special):
printf 'INFO start\nWARNING disk 80%%\nERROR disk full\nINFO done\n' > app.log
printf 'ERROR\nWARNING\n' > patterns.txt
grep -F -f patterns.txt app.logOutput
WARNING disk 80% ERROR disk full
AND: all of the patterns
grep has no AND operator. Chain two greps, or use one regular expression for both orders:
printf 'disk full on db1\ndb2 disk ok\nfull backup on db1\n' > events.txt
grep disk events.txt | grep full
echo '---'
grep -E 'disk.*full|full.*disk' events.txtOutput
disk full on db1 --- disk full on db1
Useful combinations
| Command | Effect |
|---|---|
| grep -E -i 'error|warn' f | Either word, any case |
| grep -E -w 'cat|dog' f | Whole words only (no category or dogma) |
| grep -v -e DEBUG -e TRACE f | Lines matching none of the patterns |
| grep -E -c 'error|warn' f | Count matching lines |
| grep -E -o 'error|warn' f | Print only the matched parts, one per line |
AND in one pass with awk
When the order of the words is unknown and there are more than two, a regular expression with every permutation gets long. awk tests each condition separately on the same line:
printf 'disk full on db1\ndb2 disk ok\nfull backup on db1\n' > events.txt
awk '/disk/ && /full/ && /db1/' events.txtOutput
disk full on db1
Which form to use
| Form | Use it when |
|---|---|
| grep -e A -e B | A few patterns, each a full regex; works in GNU and BSD grep |
| grep -E 'A|B' | Short alternations, or groups such as (dev|prod)-db |
| grep 'A\|B' | Only with GNU grep; BSD grep on macOS does not support \| in basic regex |
| grep -F -f list.txt | Many fixed strings, such as a list of IDs or IP addresses |
| grep -P 'A|B' | Perl-compatible syntax such as lookarounds; GNU grep built with PCRE only |
Patterns that contain regex metacharacters (., [, *) need escaping, or -F to treat them literally. An unescaped [ produces grep: Unmatched [. To search only some file types recursively with several patterns, combine these forms with grep -r --include.
egrep is an old name for grep -E; GNU grep 3.8 and later print a warning that it is obsolescent, so use grep -E in scripts.
FAQ
Why does grep 'foo|bar' print nothing?
Without -E, grep uses basic regular expressions, where | is an ordinary character. Use grep -E 'foo|bar', grep -e foo -e bar, or with GNU grep the escaped form grep 'foo\|bar'.
How do I search for a pattern that starts with a dash?
Pass it with -e, as in grep -e '-v' file, or end the options with --, as in grep -- '-v' file. Otherwise grep reads the pattern as an option.
How do I see which pattern matched on each line?
Add -o to print only the matched text, for example grep -E -o 'ERROR|WARNING' app.log | sort | uniq -c counts how often each pattern occurred.