Contare il numero di occorrenze di un pattern in un file (anche sulla stessa riga)


94

Quando cerco il numero di occorrenze di una stringa in un file, generalmente utilizzo:

grep pattern file | wc -l

Tuttavia, questo trova solo un'occorrenza per riga, a causa del modo in cui funziona grep. Come posso cercare il numero di volte in cui una stringa appare in un file, indipendentemente dal fatto che si trovino sulla stessa riga o su righe diverse?

Inoltre, cosa succede se sto cercando un pattern regex, non una semplice stringa? Come posso contarli o, ancora meglio, stampare ogni corrispondenza su una nuova riga?

Risposte:


156

Per contare tutte le occorrenze, usa -o. Prova questo:

echo afoobarfoobar | grep -o foo | wc -l

E man grepovviamente (:

Aggiornare

Alcuni suggeriscono di usare solo al grep -co fooposto di grep -o foo | wc -l.

Non farlo.

Questa scorciatoia non funzionerà in tutti i casi. La pagina man dice:

-c print a count of matching lines

La differenza in questi approcci è illustrata di seguito:

1.

$ echo afoobarfoobar | grep -oc foo
1

Non appena la corrispondenza viene trovata nella riga ( a{foo}barfoobar), la ricerca si interrompe. È stata controllata solo una riga e corrisponde, quindi l'output è 1. In realtà -oè ignorato qui e potresti semplicemente usare grep -cinvece.

2.

$ echo afoobarfoobar | grep -o foo
foo
foo

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

Sono state trovate due corrispondenze in line ( a{foo}bar{foo}bar) perché abbiamo chiesto esplicitamente di trovare ogni occorrenza ( -o). Ogni occorrenza viene stampata su una riga separata e wc -lconta solo il numero di righe nell'output.


1
Wow ... è davvero così semplice?
jrdioko

1
grep -oc non funziona in questo caso. Prova echo afoobarfoobar | grep -oc foo
Paulus

Non c'è modo di farlo per più file? Diciamo che voglio vedere il numero di occorrenze per file su un insieme di file. Posso farlo per riga con grep -c *, ma non per istanza.
Keith Tyler

grep -o foo a.txt b.txt | sort | uniq -cfunziona bene (con GNU grep): gist.github.com/hudolejev/81a05791f38cbacfd4de3ee3b44eb4f8
hudolejev

2

Prova questo:

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

Campione:

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]

1

Un post tardivo:
usa il pattern regex di ricerca come record separator (RS) in awk
questo consente alla tua regex di estendersi \nsu righe delimitate (se ne hai bisogno).

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

0

Ripgrep , che è un'alternativa veloce a grep, ha appena introdotto il --count-matchesflag che consente di contare ogni corrispondenza nella versione 0.9 (sto usando l'esempio sopra per rimanere coerente):

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

Come richiesto da OP, ripgrep consente anche il pattern regex ( --regexp <PATTERN>). Inoltre può stampare ogni (riga) corrispondenza su una riga separata:

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

-1

Attacca la funzione colore di grep e conta quanti tag di colore stampa:

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
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.