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
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 moetsort
gebruiken voordat je naaruniq
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.
grep
is opgegeven, maar voor iedereen dieack
gebruikt, is het antwoord simpelwegack -ch <pattern>
.