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

  • So che grep è specificato, ma per chiunque utilizzi ack, la risposta è semplicemente ack -ch <pattern>.
  • @KyleStrand For me ack -ch < pattern > contava solo le righe con le occorrenze e non il numero di occorrenze
  • @MarcKees Guardando la pagina man, sembra il comportamento corretto. Grazie per averlo fatto notare!

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, devi sort prima di fornire informazioni a uniq 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.

Lascia un commento

Il tuo indirizzo email non sarà pubblicato. I campi obbligatori sono contrassegnati *