Подсчитать количество вхождений шаблона в файл (даже в одной строке)

94

При поиске количества вхождений строки в файл я обычно использую:

grep pattern file | wc -l

Однако это находит только одно вхождение в строке из-за того, как работает grep. Как я могу найти, сколько раз строка появляется в файле, независимо от того, находятся ли они в одной или разных строках?

Кроме того, что, если я ищу шаблон регулярного выражения, а не простую строку? Как я могу их подсчитать или, что еще лучше, вывести каждое совпадение с новой строки?

Jrdioko
источник

Ответы:

157

Чтобы подсчитать все вхождения, используйте -o. Попробуй это:

echo afoobarfoobar | grep -o foo | wc -l

И man grepконечно (:

Обновить

Некоторые предлагают использовать просто grep -co fooвместо grep -o foo | wc -l.

Не надо.

Этот ярлык работает не во всех случаях. На странице руководства говорится:

-c print a count of matching lines

Различия в этих подходах проиллюстрированы ниже:

1.

$ echo afoobarfoobar | grep -oc foo
1

Как только совпадение будет найдено в строке ( a{foo}barfoobar), поиск прекращается. Была проверена только одна строка, и она совпала, поэтому результат есть 1. Фактически -oздесь игнорируется, и вы можете просто использовать grep -cвместо него.

2.

$ echo afoobarfoobar | grep -o foo
foo
foo

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

В строке ( a{foo}bar{foo}bar) найдено два совпадения, потому что мы явно просили найти каждое вхождение ( -o). Каждое вхождение печатается в отдельной строке и wc -lпросто подсчитывает количество строк в выводе.

Худолеев
источник
1
Вау ... это действительно так просто?
jrdioko
1
grep -oc в этом случае не работает. Попробуйте echo afoobarfoobar | grep -oc foo
Паулюс
Нет возможности сделать это для нескольких файлов? Скажем, я хочу увидеть количество вхождений каждого файла в набор файлов. Я могу сделать это для каждой строки с помощью grep -c *, но не для каждого экземпляра.
Кейт Тайлер
grep -o foo a.txt b.txt | sort | uniq -cотлично работает (с GNU grep): gist.github.com/hudolejev/81a05791f38cbacfd4de3ee3b44eb4f8
hudolejev
2

Попробуй это:

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

Образец:

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]
IBrew, поэтому
источник
1

Запоздалый пост:
используйте шаблон регулярного выражения поиска в качестве разделителя записей (RS). awk
Это позволяет вашему регулярному выражению охватывать \nограниченные строки (если вам это нужно).

printf 'X \n moo X\n XX\n' | 
   awk -vRS='X[^X]*X' 'END{print (NR<2?0:NR-1)}'
Питер.О
источник
0

Ripgrep , быстрая альтернатива grep, только что представил --count-matchesфлаг, позволяющий подсчитывать каждое совпадение в версии 0.9 (я использую приведенный выше пример, чтобы оставаться последовательным):

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

По просьбе OP, ripgrep также позволяет использовать шаблон регулярного выражения ( --regexp <PATTERN>). Также он может выводить каждое совпадение (строку) в отдельной строке:

> echo -e "line1foo\nline2afoobarfoobar" | rg foo
line1foo
line2afoobarfoobar
Себастьян Мюллер
источник
-1

Взломайте функцию цвета grep и посчитайте, сколько цветных тегов она распечатает:

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
Шиззмо
источник