Risposte:
grep's -oprodurrà solo le partite, ignorando le linee; wcpuò 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
\be \Bcosa fa qui?
uniqrimuove solo le linee identiche adiacenti, è necessario sortprima di alimentare uniqse 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 sede 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 ^needleo \bneedlein needleneedle).
sed 's/needle/\n&\n/g' | grep -cx 'needle'
Si noti che nelle sostituzioni sed di cui sopra, volevo \ndire 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 needlecome separatore di campo:
awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'
Se si desidera abbinare needleseguito 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>.