it-swarm.it

Conta il numero totale di occorrenze utilizzando grep

grep -c è utile per determinare quante volte si verifica una stringa in un file, ma conta ogni ricorrenza una sola volta per riga. Come contare più ricorrenze per riga?

Sto cercando qualcosa di più elegante di:

Perl -e '$_ = <>; print scalar ( () = m/needle/g ), "\n"'
242
user4518

grep's -o produrrà solo le corrispondenze, ignorando le righe; wc può contarli:

grep -o 'needle' file | wc -l

Ciò corrisponderà anche a "aghi" o "a più punte".

Per abbinare solo parole singole utilizzare uno dei seguenti comandi:

grep -ow 'needle' file | wc -l
grep -o '\bneedle\b' file | wc -l
grep -o '\<needle\>' file | wc -l
352
wag

Se hai GNU grep (sempre su Linux e Cygwin, occasionalmente altrove), puoi contare le linee di output da grep -o : grep -o needle | wc -l.

Con Perl, ecco alcuni modi che trovo più eleganti dei tuoi (anche dopo che è 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, trasforma prima ogni carattere non Word 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 per 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 sopra, ho usato \n per indicare una nuova riga. Questo è standard nella parte del modello, ma nel testo sostitutivo, per la portabilità, sostituire backslash-newline con \n.

Se, come me, in realtà volessi "entrambi; ognuno esattamente una volta", (questo è in realtà "o; due volte") quindi è 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.

5
OJFord

Un'altra soluzione che utilizza awk e needle come separatore di campi:

awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'

Se si desidera abbinare needle seguito da 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 alfabetici.

3
ripat

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
1
Felipe

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}' 
1
jsbillings