Policz liczbę wystąpień wzorca w pliku (nawet w tej samej linii)

94

Szukając liczby wystąpień łańcucha w pliku, generalnie używam:

grep pattern file | wc -l

Jednak to znajduje tylko jedno wystąpienie w wierszu, ze względu na sposób działania grep. Jak mogę wyszukać, ile razy ciąg znaków pojawia się w pliku, niezależnie od tego, czy znajduje się w tych samych, czy w różnych wierszach?

A co, jeśli szukam wzorca wyrażenia regularnego, a nie prostego ciągu? Jak mogę je policzyć, a nawet lepiej wydrukować każde dopasowanie w nowej linii?

jrdioko
źródło

Odpowiedzi:

157

Aby policzyć wszystkie wystąpienia, użyj -o. Spróbuj tego:

echo afoobarfoobar | grep -o foo | wc -l

I man grepoczywiście (:

Aktualizacja

Niektórzy sugerują użycie po prostu grep -co foozamiast grep -o foo | wc -l.

Nie.

Ten skrót nie będzie działał we wszystkich przypadkach. Strona podręcznika mówi:

-c print a count of matching lines

Różnicę między tymi podejściami przedstawiono poniżej:

1.

$ echo afoobarfoobar | grep -oc foo
1

Po znalezieniu dopasowania w linii ( a{foo}barfoobar) wyszukiwanie zatrzymuje się. Tylko jedna linia została sprawdzona i pasowała, więc wynik jest 1. Właściwie -ojest tutaj ignorowany i możesz po prostu użyć grep -czamiast tego.

2.

$ echo afoobarfoobar | grep -o foo
foo
foo

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

W linii ( a{foo}bar{foo}bar) znaleziono dwa dopasowania, ponieważ wyraźnie poprosiliśmy o znalezienie każdego wystąpienia ( -o). Każde wystąpienie jest drukowane w osobnym wierszu i wc -lzlicza tylko liczbę wierszy w wyniku.

hudolejev
źródło
1
Wow ... czy to naprawdę takie proste?
jrdioko
1
grep -oc nie działa w tym przypadku. Spróbuj echo afoobarfoobar | grep -oc foo
Paulus
Czy nie ma sposobu, aby to zrobić dla wielu plików? Powiedzmy, że chcę zobaczyć liczbę wystąpień na plik w zestawie plików. Mogę to zrobić dla każdego wiersza za pomocą grep -c *, ale nie dla każdego wystąpienia.
Keith Tyler,
grep -o foo a.txt b.txt | sort | uniq -cdziała dobrze (z GNU grep): gist.github.com/hudolejev/81a05791f38cbacfd4de3ee3b44eb4f8
hudolejev
2

Spróbuj tego:

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

Próba:

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]
IBrewThereforeIAm
źródło
1

Spóźniony post:
Użyj wzorca wyszukiwania jako separatora rekordów (RS) w awk
To pozwala twojemu wyrażeniu regularnemu na \nlinie z ograniczeniami w zakresie (jeśli tego potrzebujesz).

printf 'X \n moo X\n XX\n' | 
   awk -vRS='X[^X]*X' 'END{print (NR<2?0:NR-1)}'
Peter.O
źródło
0

Ripgrep , który jest szybką alternatywą dla grepa , właśnie wprowadził --count-matchesflagę umożliwiającą liczenie każdego dopasowania w wersji 0.9 (używam powyższego przykładu, aby zachować spójność):

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

Zgodnie z zapytaniem OP, ripgrep pozwala również na wzorzec wyrażenia regularnego ( --regexp <PATTERN>). Może również wydrukować każde dopasowanie (wiersz) w osobnym wierszu:

> echo -e "line1foo\nline2afoobarfoobar" | rg foo
line1foo
line2afoobarfoobar
Sebastian Müller
źródło
-1

Zhakuj funkcję koloru grepa i policz, ile kolorowych tagów wydrukuje:

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
Shizzmo
źródło