grep -c
er nyttig for å finne hvor mange ganger en streng forekommer i en fil , men det teller bare hver forekomst en gang per linje. Hvordan teller jeg flere forekomster per linje?
Jeg ser etter noe mer elegant enn:
perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n""
Kommentarer
Svar
grep «s -o
sender bare ut fyrstikkene, ignorerer linjer; wc
kan telle dem:
grep -o "needle" file | wc -l
Dette vil også matche «nåler» eller «flernål».
For å matche bare enkeltord, bruk en av følgende kommandoer:
grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l
Kommentarer
- Merk at dette krever GNU grep (Linux, Cygwin, FreeBSD, OSX).
- @wag Hvilken magi gjør
\b
og\B
gjøre her? - @Geek \ b samsvarer med en ordgrense, \ B samsvarer IKKE med en ordgrense. Svaret ovenfor ville være mer riktig hvis det ble brukt \ b i begge ender.
- For å telle forekomster per linje, kombiner med grep -n alternativet og uniq -c … grep -no ' \ < nål \ > ' fil | uniq -c
- @jameswarren
uniq
fjerner bare tilstøtende identiske linjer, du måsort
før du mater tiluniq
hvis du ikke allerede er sikker på at duplikater alltid vil være ved siden av.
Svar
Hvis du har GNU grep (alltid på Linux og Cygwin, noen ganger andre steder), kan du telle utgangslinjene fra grep -o
: grep -o needle | wc -l
.
Med Perl er det noen måter jeg finner mer elegant enn din (selv etter at den er fast ).
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 bare POSIX-verktøy er en tilnærming, hvis mulig, å dele opp skriv inn linjer med en enkelt kamp før du sender den til grep. Hvis du for eksempel «leter etter hele ord, må du først gjøre om alle tegn som ikke er ord til en ny linje.
# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$"
Ellers er det ingen standardkommando for å gjøre dette s spesielle bit tekstbehandling, så du må slå til sed (hvis du er 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
Her «er en enklere løsning ved å bruke sed
og grep
, som fungerer for strenger eller til og med boken regulære uttrykk, men mislykkes i noen få hjørnesaker med forankrede mønstre ( f.eks den finner to forekomster av ^needle
eller \bneedle
i needleneedle
).
sed "s/needle/\n&\n/g" | grep -cx "needle"
Merk at i sed-erstatningene ovenfor brukte jeg \n
for å bety en ny linje. Dette er standard i mønsterdelen, men i erstatningsteksten, for bærbarhet, erstat backslash-newline for \n
.
Svar
Hvis du, i likhet med meg, faktisk ville ha «begge deler, hver gang en gang», (dette er faktisk «enten; to ganger»), så er det enkelt :
grep -E "thing1|thing2" -c
og se etter utgangen 2
.
Fordelen med denne tilnærmingen (hvis nøyaktig en gang er det du vil ha) er at den skalerer lett.
Kommentarer
- I ' er jeg ikke sikker på at du ' faktisk sjekker den ' bare vises en gang? Alt du ' å lete etter det er at et av disse ordene eksisterer minst en gang.
- Dette bør være det aksepterte svaret. Ingen grunn til å bruke
wc -l
,grep
har et innebygd alternativ for å telle ting, og det blir til og med kalt som åpenbart som-c
for «count»!
Svar
En annen løsning ved hjelp av awk og needle
som feltseparator:
awk -F"^needle | needle | needle$" "{c+=NF-1}END{print c}"
Hvis du vil matche needle
etterfulgt av tegnsetting, endre feltutskilleren tilsvarende dvs.
awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}"
Eller bruk klassen: [^[:alnum:]]
for å omfatte alle ikke-alfabetegn.
Kommentarer
- Merk at dette krever en awk som støtter regexp-feltadskillere (for eksempel GNU awk).
Svar
Dette er min rene 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
Eksemplet ditt skriver bare ut antall forekomster per linje, og ikke summen i filen. Hvis det er det du vil, kan noe slikt fungere:
perl -nle "$c+=scalar(()=m/needle/g);END{print $c}"
Kommentarer
- Du er riktig – eksempelet mitt teller bare hendelsene i første linje.
grep
er spesifisert, men for alle som brukerack
, er svaret ganske enkeltack -ch <pattern>
.