grep -c
è utile per trovare quante volte una stringa ricorre in un file , ma conta solo ogni occorrenza una volta per riga. Come contare più occorrenze per riga?
Sto cercando qualcosa di più elegante di:
perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n""
Commenti
Risposta
grep “s -o
produrrà solo le corrispondenze, ignorando le righe; wc
può contarli:
grep -o "needle" file | wc -l
Questo corrisponderà anche a “needles” o “multineedle”.
Per trovare solo parole singole, utilizza uno dei seguenti comandi:
grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l
Commenti
- Nota che questo richiede GNU grep (Linux, Cygwin, FreeBSD, OSX).
- @wag What magic does
\b
e\B
fare qui? - @Geek \ b corrisponde a un confine di parola, \ B a NON a un confine di parola. La risposta sopra sarebbe più corretta se usasse \ b ad entrambe le estremità.
- Per un conteggio delle occorrenze per riga, combinare con grep -n opzione e uniq -c … grep -no ' \ < needle \ > ' file | uniq -c
- @jameswarren
uniq
rimuove solo le righe identiche adiacenti, devisort
prima di fornire informazioni auniq
se non sei già sicuro che i duplicati saranno sempre immediatamente adiacenti.
Rispondi
Se hai GNU grep (sempre su Linux e Cygwin, occasionalmente altrove), puoi contare le righe di output da grep -o
: grep -o needle | wc -l
.
Con Perl, ecco alcuni modi in cui trovo più elegante del tuo (anche dopo che “s fixed ).
perl -lne "END {print $c} map ++$c, /needle/g" perl -lne "END {print $c} $c += s/needle//g" perl -lne "END {print $c} ++$c while /needle/g"
Con solo strumenti POSIX, un approccio, se possibile, è dividere il input in righe con una singola corrispondenza prima di passarla a grep. Ad esempio, se stai cercando parole intere, prima trasforma ogni carattere non alfanumerico in una nuova riga.
# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$"
Altrimenti, non esiste un comando standard per farlo è una parte particolare dellelaborazione del testo, quindi devi passare a sed (se sei un masochista) o awk.
awk "{while (match($0, /set/)) {++c; $0=substr($0, RSTART+RLENGTH)}} END {print c}" sed -n -e "s/set/\n&\n/g" -e "s/^/\n/" -e "s/$/\n/" \ -e "s/\n[^\n]*\n/\n/g" -e "s/^\n//" -e "s/\n$//" \ -e "/./p" | wc -l
Qui “una soluzione più semplice usando sed
e grep
, che funziona per le stringhe o anche per le espressioni regolari da manuale, ma fallisce in alcuni casi angolari con modelli ancorati ( per esempio trova due occorrenze di ^needle
o \bneedle
in needleneedle
).
sed "s/needle/\n&\n/g" | grep -cx "needle"
Nota che nelle sostituzioni sed sopra, ho usato \n
per indicare una nuova riga. Questo è standard nella parte del modello, ma nel testo sostitutivo, per la portabilità, sostituire la barra rovesciata-nuova riga con \n
.
Risposta
Se, come me, in realtà volevi “entrambi; ciascuno esattamente una volta”, (questo è in realtà “uno; due volte”) allora è semplice :
grep -E "thing1|thing2" -c
e controlla loutput 2
.
Il vantaggio di questo approccio (se esattamente una volta è ciò che desideri) è che si ridimensiona facilmente.
Commenti
- I ' Non sono sicuro che ' stia effettivamente controllando ' che compaia solo una volta? Tutti ' se stai cercando una di queste parole esiste almeno una volta.
- Questa dovrebbe essere la risposta accettata. Non è necessario utilizzare
wc -l
,grep
ha unopzione incorporata per contare le cose, ed è persino chiamata ovvia come-c
per “count”!
Answer
Un altro soluzione utilizzando awk e needle
come separatore di campo:
awk -F"^needle | needle | needle$" "{c+=NF-1}END{print c}"
Se desideri trovare la corrispondenza con needle
seguito dalla punteggiatura, modifica il separatore di campo di conseguenza, ad esempio
awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}"
Oppure utilizza la classe: [^[:alnum:]]
per racchiudere tutti i caratteri non alfabetici.
Commenti
- Nota che questo richiede un awk che supporti i separatori di campo regexp (come GNU awk).
Risposta
Questa è la mia pura soluzione bash
#!/bin/bash B=$(for i in $(cat /tmp/a | sort -u); do echo "$(grep $i /tmp/a | wc -l) $i" done) echo "$B" | sort --reverse
Risposta
Il tuo esempio stampa solo il numero di occorrenze per riga e non il totale nel file. Se è quello che vuoi, qualcosa del genere potrebbe funzionare:
perl -nle "$c+=scalar(()=m/needle/g);END{print $c}"
Commenti
- Tu hanno ragione: il mio esempio conta solo le occorrenze nella prima riga.
grep
è specificato, ma per chiunque utilizziack
, la risposta è semplicementeack -ch <pattern>
.