grep -c
är användbart för att hitta hur många gånger en sträng förekommer i en fil , men det räknar bara varje förekomst en gång per rad. Hur räknar jag flera förekomster per rad?
Jag letar efter något mer elegant än:
perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n""
Kommentarer
Svar
grep ”s -o
matar bara ut matchningarna och ignorerar rader; wc
kan räkna dem:
grep -o "needle" file | wc -l
Detta kommer också att matcha ”nålar” eller ”multinedle”.
Använd en av följande kommandon för att matcha enstaka ord:
grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l
Kommentarer
- Observera att detta kräver GNU grep (Linux, Cygwin, FreeBSD, OSX).
- @wag Vilken magi gör
\b
och\B
gör här? - @Geek \ b matchar en ordgräns, \ B matchar INTE en ordgräns. Svaret ovan skulle vara mer korrekt om det användes \ b i båda ändar.
- För ett antal förekomster per rad, kombinera med grep -n-alternativet och uniq -c … grep -no ' \ < nål \ > ' fil | uniq -c
- @jameswarren
uniq
tar bara bort intilliggande identiska rader, du måstesort
innan du matar tilluniq
om du inte redan är säker på att dubbletter alltid kommer att vara direkt intill.
Svar
Om du har GNU grep (alltid på Linux och Cygwin, ibland någon annanstans) kan du räkna utgångslinjerna från grep -o
: grep -o needle | wc -l
.
Med Perl är det här några sätt jag tycker är elegantare än din (även efter det ”s fixad ).
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"
Med endast POSIX-verktyg är en metod, om möjligt, att dela upp mata in rader med en enda matchning innan du skickar den till grep. Till exempel, om du letar efter hela ord, förvandlar du först alla icke-ordstecken till en ny rad.
# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$"
Annars finns det inget standardkommando att göra detta s speciella bit textbehandling, så du måste vända dig till sed (om du är en masochist) eller 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
Här ”är en enklare lösning med sed
och grep
, som fungerar för strängar eller till och med böcker, reguljära uttryck men misslyckas i några hörnfall med förankrade mönster ( t.ex den hittar två förekomster av ^needle
eller \bneedle
i needleneedle
).
sed "s/needle/\n&\n/g" | grep -cx "needle"
Observera att i sed-substitutionerna ovan använde jag \n
för att betyda en ny rad. Detta är standard i mönsterdelen, men i ersättningstexten, för bärbarhet, ersätt backslash-newline för \n
.
Svar
Om du, precis som jag, verkligen ville ha ”båda, var och en exakt en gång”, (detta är faktiskt ”antingen; två gånger”) så är det enkelt :
grep -E "thing1|thing2" -c
och leta efter utdata 2
.
Fördelen med denna metod (om exakt en gång är vad du vill ha) är att den enkelt skalas.
Kommentarer
- I ' Är jag inte säker på att du ' faktiskt kontrollerar att den ' bara visas en gång? Allt du ' att leta efter finns att något av dessa ord existerar minst en gång.
- Detta borde vara det accepterade svaret. Inget behov av att använda
wc -l
,grep
har ett inbyggt alternativ för att räkna saker, och det kallas till och med som självklart som-c
för ”count”!
Svar
Ytterligare lösning med awk och needle
som fältseparator:
awk -F"^needle | needle | needle$" "{c+=NF-1}END{print c}"
Om du vill matcha needle
följt av skiljetecken, ändra fältseparatorn i enlighet därmed dvs.
awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}"
Eller använd klassen: [^[:alnum:]]
för att omfatta alla icke-alfabetecken.
Kommentarer
- Observera att detta kräver en awk som stöder regexp-fältavgränsare (t.ex. GNU awk).
Svar
Detta är min rena bash-lösning
#!/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
Svar
Ditt exempel skriver bara ut antalet förekomster per rad och inte summan i filen. Om det är vad du vill kan något liknande fungera:
perl -nle "$c+=scalar(()=m/needle/g);END{print $c}"
Kommentarer
- Du har rätt – mitt exempel räknar bara förekomsten i första raden.
grep
anges, men för alla som använderack
är svaret helt enkeltack -ch <pattern>
.