Skip to content

/flags/grep/grep-o-only-matching

grep -o: print only the matching part of each line

verified onenvironmentlocal · windows 10.0.26200 arm64 · GNU bash 5.3.15 (Git Bash) · grep (GNU grep) 3.0updated
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

bash
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.log

Output

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:

bash
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 -c

Output

1
2
      1 ERROR 500

For frequency tables of extracted values, the pattern is the one in sort | uniq -c.

Options that work with -o

OptionEffect with -o
-nPrefix each match with its line number
-bPrefix each match with its byte offset in the file
-iCase-insensitive; prints the match as it appears in the input
-EExtended regex, so +, ? and {n} work without backslashes
-wOnly matches that form whole words
-h / -HHide or show the file name before each match
-vPrints nothing: inverted lines have no matching part to show (exit status still 0)
-A, -B, -CContext 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. -o prints the whole match. GNU grep with -P can use \K and lookarounds; BSD grep on macOS has no -P.
  • Overlapping matches are not reported. After a match grep continues after its end, so aa in aaa matches once.
  • Binary files. If grep decides the input is binary, it prints Binary file ... matches instead of the matches; see Binary file matches and use -a for 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.

Sources