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

  • Jeg vet at grep er spesifisert, men for alle som bruker ack, er svaret ganske enkelt ack -ch <pattern>.
  • @KyleStrand For meg ack -ch < mønster > bare telt linjene med forekomster og ikke antall forekomster
  • @MarcKees Ser på mannssiden, det høres ut som riktig oppførsel. Takk for at du påpekte det!

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 til uniq 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.

Legg igjen en kommentar

Din e-postadresse vil ikke bli publisert. Obligatoriske felt er merket med *