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

  • Jeg ved, at grep er angivet, men for alle, der bruger ack, er svaret simpelthen ack -ch <pattern>.
  • @KyleStrand For mig ack -ch < mønster > tællede kun linjerne med forekomster og ikke antallet af forekomster
  • @MarcKees Ser man på mandsiden, det lyder som den rigtige adfærd. Tak for at påpege det!

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 skal sort før du fodrer til uniq 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.

Skriv et svar

Din e-mailadresse vil ikke blive publiceret. Krævede felter er markeret med *