grep -c
er nyttigt til at finde ud af, hvor mange gange en streng forekommer i en fil , men det tæller kun hver forekomst en gang pr. linje. Hvordan tæller jeg flere forekomster pr. Linje?
Jeg leder efter noget mere elegant end:
perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n""
Kommentarer
Svar
grep “s -o
udsender kun matches, ignorerer linjer; wc
kan tælle dem:
grep -o "needle" file | wc -l
Dette vil også matche “nåle” eller “multineedle”.
Brug kun en af følgende kommandoer til at matche enkelt ord:
grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l
Kommentarer
- Bemærk, at dette kræver GNU grep (Linux, Cygwin, FreeBSD, OSX).
- @wag Hvilken magi gør
\b
og\B
gøre her? - @Geek \ b matcher en ordgrænse, \ B matcher IKKE en ordgrænse. Svaret ovenfor ville være mere korrekt, hvis det bruges \ b i begge ender.
- For et antal forekomster pr. Linje kombineres med grep -n option og uniq -c … grep -no ' \ < nål \ > ' fil | uniq -c
- @jameswarren
uniq
fjerner kun tilstødende identiske linjer, du skalsort
før du fodrer tiluniq
hvis du ikke allerede er sikker på, at duplikater altid vil være umiddelbart ved siden af.
Svar
Hvis du har GNU grep (altid på Linux og Cygwin, lejlighedsvis andre steder), kan du tælle outputlinjerne fra grep -o
: grep -o needle | wc -l
.
Med Perl er her et par måder, jeg finder mere elegant end din (selv efter det “s 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 kun POSIX-værktøjer er en tilgang, hvis det er muligt, at opdele indtast linjer med et enkelt match, før du sender det til grep. Hvis du f.eks. “leder efter hele ord, skal du først omdanne hvert ikke-ordtegn til en ny linje.
# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$"
Ellers er der ingen standardkommando til at udføre dette s særlige smule tekstbehandling, så du skal henvende dig 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 hjælp af sed
og grep
, som fungerer for strenge eller endda bog-regulære udtryk, men mislykkes i nogle få hjørnesager med forankrede mønstre ( for eksempel den finder to forekomster af ^needle
eller \bneedle
i needleneedle
).
sed "s/needle/\n&\n/g" | grep -cx "needle"
Bemærk, at i sed-substitutionerne ovenfor brugte jeg \n
til at betyde en ny linje. Dette er standard i mønsterdelen, men i erstatningsteksten, for bærbarhed, erstat backslash-newline for \n
.
Svar
Hvis du ligesom mig faktisk ville have “begge; hver især nøjagtigt en gang”, (dette er faktisk “enten; to gange”), så er det simpelt :
grep -E "thing1|thing2" -c
og tjek for output 2
.
Fordelen ved denne tilgang (hvis nøjagtigt en gang er hvad du vil have) er, at det skaleres let.
Kommentarer
- I ' er jeg ikke sikker på, at du ' faktisk kontrollerer det ' kun vises en gang? Alt du ' at lede efter der er, at et af disse ord findes mindst én gang.
- Dette skal være det accepterede svar. Ingen grund til at bruge
wc -l
,grep
har en indbygget mulighed for at tælle ting, og det kaldes endda som indlysende som-c
for “count”!
Svar
En anden løsning ved hjælp af awk og needle
som feltseparator:
awk -F"^needle | needle | needle$" "{c+=NF-1}END{print c}"
Hvis du vil matche needle
efterfulgt af tegnsætning, skift feltudskilleren i overensstemmelse hermed, dvs.
awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}"
Eller brug klassen: [^[:alnum:]]
for at omfatte alle ikke-alfabetegn.
Kommentarer
- Bemærk, at dette kræver et awk, der understøtter regexp-feltadskillere (såsom 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
Dit eksempel udskriver kun antallet af forekomster pr. linje og ikke det samlede antal i filen. Hvis det er det, du vil, kan noget lignende fungere:
perl -nle "$c+=scalar(()=m/needle/g);END{print $c}"
Kommentarer
- Dig er rigtige – mit eksempel tæller kun forekomsterne i første linje.
grep
er angivet, men for alle, der brugerack
, er svaret simpelthenack -ch <pattern>
.