/flags/grep/grep-o-only-matching
grep -o: print only the matching part of each line
- tool
- grep
- command
- grep
- flag
- -o, --only-matching
- default
- off (the whole matching line is printed)
grep -o PATTERN file prints only the text that matched, one match per output line, instead of the whole line. A line with three matches produces three lines of output. It is the quickest way to extract IDs, URLs, IP addresses or error codes from logs, and it combines well with sort | uniq -c to count them.
Whole line vs -o
printf 'id=42 user=ana id=7\nno ids here\nERROR 500 at /api\n' > app.log
grep 'id=[0-9]*' app.log
echo '--- with -o'
grep -o 'id=[0-9]*' app.log
echo '--- with -o and -n'
grep -on 'id=[0-9]*' app.logOutput
id=42 user=ana id=7 --- with -o id=42 id=7 --- with -o and -n 1:id=42 1:id=7
The first line contains two matches, so -o prints two lines, and -n shows that both come from line 1.
Counting matches
-c counts matching lines, and it keeps doing that with -o. To count occurrences, count the output lines:
printf 'id=42 user=ana id=7\nno ids here\nERROR 500 at /api\n' > app.log
grep -c -o 'id=[0-9]*' app.log
grep -o 'id=[0-9]*' app.log | wc -l
grep -oE '[A-Z]+ [0-9]{3}' app.log | sort | uniq -cOutput
1
2
1 ERROR 500For frequency tables of extracted values, the pattern is the one in sort | uniq -c.
Options that work with -o
| Option | Effect with -o |
|---|---|
| -n | Prefix each match with its line number |
| -b | Prefix each match with its byte offset in the file |
| -i | Case-insensitive; prints the match as it appears in the input |
| -E | Extended regex, so +, ? and {n} work without backslashes |
| -w | Only matches that form whole words |
| -h / -H | Hide or show the file name before each match |
| -v | Prints nothing: inverted lines have no matching part to show (exit status still 0) |
| -A, -B, -C | Context lines are not printed, only the matches |
Pitfalls
- Empty matches are dropped. A pattern that can match the empty string, such as
[0-9]*, may report a match without printing anything. Write[0-9][0-9]*or-E '[0-9]+'. - Greedy matching.
grep -o '".*"'on a line with two quoted strings prints everything from the first quote to the last. Use a negated class:'"[^"]*"'. - No capture groups.
-oprints the whole match. GNU grep with-Pcan use\Kand lookarounds; BSD grep on macOS has no-P. - Overlapping matches are not reported. After a match grep continues after its end, so
aainaaamatches once. - Binary files. If grep decides the input is binary, it prints
Binary file ... matchesinstead of the matches; see Binary file matches and use-afor text with odd bytes.
-o exists in GNU grep, BSD grep and BusyBox, but it is not part of POSIX, so very minimal systems may lack it.
FAQ
How do I count every match, not every matching line?
grep -c counts lines, even with -o. Pipe the -o output to wc -l instead: grep -o 'pattern' file | wc -l counts each occurrence, including several on the same line.
How do I print only a capture group with grep?
grep -o prints the whole match; it has no capture groups. With GNU grep built with PCRE, grep -oP 'id=\K[0-9]+' drops the part before \K. Otherwise use sed -n 's/.*id=\([0-9]*\).*/\1/p' or awk.
Why does grep -o print nothing for a pattern like 'x*'?
x* also matches the empty string, and grep -o does not print empty matches. Lines still count as matching, so the exit status is 0. Use x+ with -E, or xx* in a basic regex, so every match has at least one character.