Risposte:
grep's -o
produrrà solo le partite, ignorando le linee; wc
può contarli:
grep -o 'needle' file | wc -l
Ciò corrisponderà anche a "aghi" o "a più punte".
Solo parole singole:
grep -o '\bneedle\B' file | wc -l
# or:
grep -o '\<needle\>' file | wc -l
\b
e \B
cosa fa qui?
uniq
rimuove solo le linee identiche adiacenti, è necessario sort
prima di alimentare uniq
se non si è già sicuri che i duplicati saranno sempre immediatamente adiacenti.
Se si dispone di GNU grep (sempre su Linux e Cygwin, di tanto in tanto altrove), è possibile contare le linee di uscita dagrep -o
: grep -o needle | wc -l
.
Con Perl, ecco alcuni modi in cui trovo più elegante del tuo (anche dopo che è stato risolto ).
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 gli strumenti POSIX, un approccio, se possibile, è quello di dividere l'input in righe con una singola corrispondenza prima di passarlo a grep. Ad esempio, se stai cercando parole intere, prima trasforma ogni carattere non composto da parole 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 eseguire questo particolare bit di elaborazione del testo, quindi è necessario 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
Ecco una soluzione più semplice che utilizza sed
e grep
, che funziona con stringhe o anche espressioni regolari del libro, ma non riesce in alcuni casi angolari con motivi ancorati (ad esempio, trova due occorrenze di ^needle
o \bneedle
in needleneedle
).
sed 's/needle/\n&\n/g' | grep -cx 'needle'
Si noti che nelle sostituzioni sed di cui sopra, volevo \n
dire una nuova riga. Questo è standard nella parte del modello, ma nel testo sostitutivo, per la portabilità, sostituire backslash-newline per \n
.
Se, come me, in realtà volevi "entrambi; ognuno esattamente una volta", (questo è in realtà "uno; due volte"), allora è semplice:
grep -E "thing1|thing2" -c
e controlla l'output 2
.
Il vantaggio di questo approccio (se esattamente una volta è quello che vuoi) è che si ridimensiona facilmente.
Un'altra soluzione che utilizza awk e needle
come separatore di campo:
awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'
Se si desidera abbinare needle
seguito dalla punteggiatura, modificare il separatore di campo di conseguenza, ad es
awk -F'^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$' '{c+=NF-1}END{print c}'
Oppure usa la classe: [^[:alnum:]]
per includere tutti i caratteri non alfa.
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}'
grep
è specificato, ma per chiunque utilizziack
, la risposta è semplicementeack -ch <pattern>
.