grep -c is handig om te bepalen hoe vaak een string in een bestand voorkomt , maar het telt maar één keer per regel. Hoe meerdere keren per regel te tellen?

Ik “ben op zoek naar iets eleganters dan:

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

Reacties

  • Ik weet dat grep is opgegeven, maar voor iedereen die ack gebruikt, is het antwoord simpelweg ack -ch <pattern>.
  • @KyleStrand Voor mij ack -ch < patroon > telde alleen de regels met voorvallen en niet het aantal voorvallen
  • @MarcKees Als je naar de man-pagina kijkt, klinkt dat als het juiste gedrag. Bedankt dat je erop wijst!

Answer

grep “s -o zal alleen de overeenkomsten weergeven, regels negerend; wc kan ze tellen:

grep -o "needle" file | wc -l 

Dit komt ook overeen met “needles” of “multineedle”.

Gebruik een van de volgende opdrachten om alleen enkele woorden te matchen:

grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l 

Opmerkingen

  • Merk op dat dit GNU grep vereist (Linux, Cygwin, FreeBSD, OSX).
  • @wag What magic does \b en \B hier doen?
  • @Geek \ b komt overeen met een woordgrens, \ B komt overeen met GEEN woordgrens. Het bovenstaande antwoord zou correcter zijn als het aan beide uiteinden \ b zou gebruiken.
  • Combineer met de optie grep -n en uniq -c … grep -no ' \ < needle \ > ' bestand | uniq -c
  • @jameswarren uniq verwijdert alleen aangrenzende identieke regels, je moet sort gebruiken voordat je naar uniq als u er niet zeker van bent dat duplicaten altijd direct aangrenzend zullen zijn.

Antwoord

Als je GNU grep hebt (altijd op Linux en Cygwin, af en toe ergens anders), kun je de uitvoerregels tellen van grep -o : grep -o needle | wc -l.

Met Perl zijn hier een paar manieren die ik eleganter vind dan die van jou (zelfs nadat het “s opgelost ).

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" 

Met alleen POSIX-tools is één benadering, indien mogelijk, het splitsen van de invoer in regels met een enkele overeenkomst voordat u deze doorgeeft aan grep. Als u bijvoorbeeld “hele woorden zoekt, zet dan eerst elk niet-woordteken om in een nieuwe regel.

# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$" 

Anders is er geen standaardcommando om dit te doen s specifiek stukje tekstverwerking, dus je moet naar sed (als je een masochist bent) of 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 

Hier is een eenvoudigere oplossing met sed en grep, die werken voor tekenreeksen of zelfs reguliere expressies uit het boekje, maar in een paar gevallen mislukt met verankerde patronen ( bijv het vindt twee exemplaren van ^needle of \bneedle in needleneedle).

sed "s/needle/\n&\n/g" | grep -cx "needle" 

Merk op dat ik in de sed-substituties hierboven \n heb gebruikt om een nieuwe regel aan te duiden. Dit is standaard in het patroongedeelte, maar in de vervangende tekst, voor portabiliteit, vervangt u backslash-newline voor \n.

Antwoord

Als je, zoals ik, eigenlijk “beide; elk precies één keer”, wilde hebben (dit is eigenlijk “ofwel; twee keer”), dan is het simpel :

grep -E "thing1|thing2" -c 

en controleer op de uitvoer 2.

Het voordeel van deze benadering (als precies één keer is wat je wilt) is dat het gemakkelijk schaalbaar is.

Opmerkingen

  • I ' m niet zeker of u ' het daadwerkelijk controleert ' en slechts één keer voorkomt? Allemaal ' bij het zoeken is er dat een van deze woorden minstens één keer bestaat.
  • Dit zou het geaccepteerde antwoord moeten zijn. , grep heeft een ingebouwde optie om dingen te tellen, en het wordt zelfs genoemd als voor de hand liggend als -c voor “count”!

Antwoord

Nog een oplossing met awk en needle als veldscheidingsteken:

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

Als je wilt matchen met needle gevolgd door interpunctie, verander het veldscheidingsteken dienovereenkomstig, dwz

awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}" 

Of gebruik de klasse: [^[:alnum:]] om alle niet-alfa-tekens te omvatten.

Opmerkingen

  • Merk op dat dit een awk vereist die regexp veldscheidingstekens ondersteunt (zoals GNU awk).

Antwoord

Dit is mijn pure bash-oplossing

#!/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 

Answer

Uw voorbeeld drukt alleen het aantal keren per regel af, en niet het totaal in het bestand. Als dat is wat je wilt, kan zoiets als dit werken:

perl -nle "$c+=scalar(()=m/needle/g);END{print $c}" 

Reacties

  • Jij hebben gelijk – mijn voorbeeld telt alleen de voorvallen in de eerste regel.

Geef een reactie

Het e-mailadres wordt niet gepubliceerd. Vereiste velden zijn gemarkeerd met *