grep -c
este util pentru a găsi de câte ori apare un șir într-un fișier , dar contează fiecare apariție o singură dată pe linie. Cum să număr mai multe apariții pe linie?
Caut ceva mai elegant decât:
perl -e "$_ = <>; print scalar ( () = m/needle/g ), "\n""
Comentarii
Răspuns
grep „s -o
va afișa doar potrivirile, ignorând liniile; wc
le poate număra:
grep -o "needle" file | wc -l
Acest lucru se va potrivi și cu „ace” sau cu „multiintroducere”.
Pentru a se potrivi doar cuvinte simple, utilizați una dintre următoarele comenzi:
grep -ow "needle" file | wc -l grep -o "\bneedle\b" file | wc -l grep -o "\<needle\>" file | wc -l
Comentarii
- Rețineți că acest lucru necesită GNU grep (Linux, Cygwin, FreeBSD, OSX).
- @wag Ce magie face
\b
și\B
faci aici? - @Geek \ b se potrivește cu o limită de cuvânt, \ B nu se potrivește cu o limită de cuvânt. Răspunsul de mai sus ar fi mai corect dacă s-ar folosi \ b la ambele capete.
- Pentru un număr de apariții pe linie, combinați cu opțiunea grep -n și uniq -c … grep -no ' \ < ac \ > ' fișier | uniq -c
- @jameswarren
uniq
elimină numai liniile identice adiacente, trebuie săsort
înainte de a alimentauniq
dacă nu sunteți deja sigur că duplicatele vor fi întotdeauna imediat adiacente.
Răspuns
Dacă aveți GNU grep (întotdeauna pe Linux și Cygwin, ocazional în altă parte), puteți număra liniile de ieșire din grep -o
: grep -o needle | wc -l
.
Cu Perl, iată câteva moduri pe care le găsesc mai elegante decât ale tale (chiar și după „s fixat ).
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"
Cu numai instrumente POSIX, o abordare, dacă este posibil, este de a împărți introduceți în linii cu o singură potrivire înainte de a-l trece la grep. De exemplu, dacă căutați cuvinte întregi, transformați mai întâi fiecare caracter care nu este un cuvânt într-o linie nouă.
# equivalent to grep -ow "needle" | wc -l tr -c "[:alnum:]" "[\n*]" | grep -c "^needle$"
În caz contrar, nu există nicio comandă standard pentru a face asta Procesul special de procesare a textului, deci trebuie să apelați la sed (dacă sunteți masochist) sau 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
Aici este o soluție mai simplă folosind sed
și grep
, care funcționează pentru șiruri sau chiar expresii obișnuite din carte, dar eșuează în câteva cazuri de colț cu modele ancorate ( de exemplu găsește două apariții ale ^needle
sau \bneedle
în needleneedle
).
sed "s/needle/\n&\n/g" | grep -cx "needle"
Rețineți că în substituțiile sed de mai sus, am folosit \n
pentru a însemna o linie nouă. Acest lucru este standard în partea de model, dar în textul de înlocuire, pentru portabilitate, înlocuiți backslash-newline cu \n
.
Răspuns
Dacă, ca și mine, ai vrut de fapt „ambele; fiecare exact o dată”, (acesta este de fapt „fie; de două ori”), atunci este simplu :
grep -E "thing1|thing2" -c
și verificați ieșirea 2
.
Beneficiul acestei abordări (dacă exact o dată este ceea ce doriți) este că acesta scară ușor.
Comentarii
- I ' Nu sunt sigur că ' îl verificați de fapt ' apare doar o dată? Toți ' Căutând că există unul dintre aceste cuvinte există cel puțin o dată.
- Acesta ar trebui să fie răspunsul acceptat. Nu este nevoie să utilizați
wc -l
,grep
are o opțiune încorporată pentru a număra lucrurile și este chiar denumită ca fiind evidentă ca-c
pentru „count”!
Răspuns
Altul soluție folosind awk și needle
ca separator de câmp:
awk -F"^needle | needle | needle$" "{c+=NF-1}END{print c}"
Dacă doriți să se potrivească cu needle
urmat de punctuație, modificați separatorul de câmp în consecință, adică
awk -F"^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$" "{c+=NF-1}END{print c}"
Sau utilizați clasa: [^[:alnum:]]
pentru a cuprinde toate caracterele non-alfa.
Comentarii
- Rețineți că acest lucru necesită un awk care acceptă separatorii de câmp regexp (cum ar fi GNU awk).
Răspuns
Aceasta este soluția mea pură bash
#!/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
Răspuns
Exemplul dvs. tipărește doar numărul de apariții pe linie și nu totalul din fișier. Dacă așa doriți, ar putea funcționa ceva de genul acesta:
perl -nle "$c+=scalar(()=m/needle/g);END{print $c}"
Comentarii
- Tu au dreptate – exemplul meu contează doar aparițiile din prima linie.
grep
, dar pentru oricine foloseșteack
, răspunsul este pur și simpluack -ch <pattern>
.