Count number of occurrences of a pattern in a file (even on same line)

SearchCountGrepMatch

Search Problem Overview


When searching for number of occurrences of a string in a file, I generally use:

grep pattern file | wc -l

However, this only finds one occurrence per line, because of the way grep works. How can I search for the number of times a string appears in a file, regardless of whether they are on the same or different lines?

Also, what if I'm searching for a regex pattern, not a simple string? How can I count those, or, even better, print each match on a new line?

Search Solutions


Solution 1 - Search

To count all occurrences, use -o. Try this:

echo afoobarfoobar | grep -o foo | wc -l

And man grep of course (:

Update

Some suggest to use just grep -co foo instead of grep -o foo | wc -l.

Don't.

This shortcut won't work in all cases. Man page says:

-c print a count of matching lines

Difference in these approaches is illustrated below:

$ echo afoobarfoobar | grep -oc foo
1

As soon as the match is found in the line (a{foo}barfoobar) the searching stops. Only one line was checked and it matched, so the output is 1. Actually -o is ignored here and you could just use grep -c instead.

$ echo afoobarfoobar | grep -o foo
foo
foo

$ echo afoobarfoobar | grep -o foo | wc -l
2

Two matches are found in the line (a{foo}bar{foo}bar) because we explicitly asked to find every occurrence (-o). Every occurence is printed on a separate line, and wc -l just counts the number of lines in the output.

Solution 2 - Search

Try this:

grep "string to search for" FileNameToSearch | cut -d ":" -f 4 | sort -n | uniq -c

Sample:

grep "SMTP connect from unknown" maillog | cut -d ":" -f 4 | sort -n | uniq -c
  6  SMTP connect from unknown [188.190.118.90]
 54  SMTP connect from unknown [62.193.131.114]
  3  SMTP connect from unknown [91.222.51.253]

Solution 3 - Search

A belated post:
Use the search regex pattern as a Record Separator (RS) in awk
This allows your regex to span \n-delimited lines (if you need it).

printf 'X \n moo X\n XX\n' | 
   awk -vRS='X[^X]*X' 'END{print (NR<2?0:NR-1)}'

Solution 4 - Search

Ripgrep, which is a fast alternative to grep, has just introduced the --count-matches flag allowing counting each match in version 0.9 (I'm using the above example to stay consistent):

> echo afoobarfoobar | rg --count foo
1
> echo afoobarfoobar | rg --count-matches foo
2

As asked by OP, ripgrep allows for regex pattern as well (--regexp <PATTERN>). Also it can print each (line) match on a separate line:

> echo -e "line1foo\nline2afoobarfoobar" | rg foo
line1foo
line2afoobarfoobar

Solution 5 - Search

Hack grep's color function, and count how many color tags it prints out:

echo -e "a\nb  b b\nc\ndef\nb e brb\nr" \
| GREP_COLOR="033" grep --color=always  b \
| perl -e 'undef $/; $_=<>; s/\n//g; s/\x1b\x5b\x30\x33\x33/\n/g; print $_' \
| wc -l

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionjrdiokoView Question on Stackoverflow
Solution 1 - SearchhudolejevView Answer on Stackoverflow
Solution 2 - SearchIBrewThereforeIAmView Answer on Stackoverflow
Solution 3 - SearchPeter.OView Answer on Stackoverflow
Solution 4 - SearchSebastian MüllerView Answer on Stackoverflow
Solution 5 - SearchShizzmoView Answer on Stackoverflow