Grep kullanarak toplam örnek sayısını say


215

grep -cBir dizgede bir dosyada kaç kez meydana geldiğini bulmak için kullanışlıdır, ancak satır başına bir kez sadece bir kez meydana geldiğini sayar. Satır başına birden çok oluşum nasıl sayılır?

Şundan daha şık bir şey arıyorum:

perl -e '$_ = <>; print scalar ( () = m/needle/g ), "\n"'

4
Biliyorum grepbelirtilmiş, ama kullanan herkes ackiçin cevap basit ack -ch <pattern>.
Kyle Strand

Yanıtlar:


302

grep'ler -oyalnızca kibritleri çıkarır, satırları yok sayar; wconları sayabilir:

grep -o 'needle' file | wc -l

Bu aynı zamanda “iğneler” veya “çok katlı” ile eşleşecektir.
Sadece tek kelimeler:

grep -o '\bneedle\B' file | wc -l
# or:
grep -o '\<needle\>' file | wc -l

6
Bunun GNU grep (Linux, Cygwin, FreeBSD, OSX) gerektirdiğini unutmayın.
Gilles

@wag sihir nedir gelmez \bve \Bburada yapmak?
Geek

6
@Geek \ b bir kelime sınırı ile eşleşir, \ B bir kelime sınırı ile eşleşmez. Her iki uçta da \ b kullanılırsa, yukarıdaki cevap daha doğru olacaktır.
Liam

1
Her satırda bir kaç defa yer almak için grep -n seçeneği ve uniq -c ... grep -no '\ <needle \>' dosyasıyla birleştirin | uniq -c
jameswarren

@jameswarren uniqyalnızca bitişik özdeş çizgileri kaldırır , çoğaltmaların her zaman hemen bitişik olacağından emin değilseniz sortbeslemeden önce yapmanız gerekir uniq.
tripleee

16

Eğer (her zaman başka bir yerde bazen Linux ve Cygwin, üzerine) GNU grep varsa, olabilir çıktı satırları saymakgrep -o : grep -o needle | wc -l.

Perl ile, sizinkinden daha şık bulmamın birkaç yolu var ( düzeltildikten sonra bile ).

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'

Yalnızca POSIX araçlarıyla, mümkünse bir yaklaşım grep'e geçmeden önce girişi tek bir eşleşmeyle satırlara bölmektir. Örneğin, tüm kelimeleri arıyorsanız, önce sözcük olmayan her karakteri yeni bir satıra dönüştürün.

# equivalent to grep -ow 'needle' | wc -l
tr -c '[:alnum:]' '[\n*]' | grep -c '^needle$'

Aksi takdirde, bu belirli bir metin işleme bitini yapmak için standart bir komut yoktur, bu yüzden sed (mazoşist iseniz) veya awk'ye dönmeniz gerekir.

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

İşte basit kullanarak çözüm sedve grepdüzenli ifadeler dizeleri hatta kitabına göre çalışır ancak (iki oluşumunu örn bulduğu demirlemiş desenleri ile birkaç köşe durumlarda başarısız ^needleveya \bneedleiçinde needleneedle).

sed 's/needle/\n&\n/g' | grep -cx 'needle'

Yukarıdaki sed atamalarında, \nyeni bir satır anlamına geldiğime dikkat edin . Bu, kalıp bölümünde standarttır, ancak yedek metinde, taşınabilirlik için ters eğik çizgi newline yerine kullanılır \n.


4

Benim gibi, sen aslında "her ikisini de; her biri tam olarak bir kez", (bu aslında "ya da iki kere") istiyorsan, basittir:

grep -E "thing1|thing2" -c

ve çıkışı kontrol edin 2.

Bu yaklaşımın faydası (tam olarak bir kez eğer olduğunu istediğini) kolayca ölçekler olmasıdır.


Aslında sadece bir kez göründüğünü kontrol ettiğinizden emin değilim? Aradığın tek şey, bu kelimelerden birinin en az bir kere var olduğudur.
Steve Gore

3

Awk ve needlealan ayırıcı olarak kullanan başka bir çözüm :

awk -F'^needle | needle | needle$' '{c+=NF-1}END{print c}'

needleNoktalama işaretinin ardından eşleştirmek istiyorsanız , alan ayırıcısını buna göre değiştirin

awk -F'^needle[ ,.?]|[ ,.?]needle[ ,.?]|[ ,.?]needle$' '{c+=NF-1}END{print c}'

Veya sınıfı kullanın: [^[:alnum:]]alfa olmayan tüm karakterleri kapsayacak şekilde.


Bunun regexp alan ayırıcılarını destekleyen bir awk gerektirdiğini unutmayın (GNU awk gibi).
Gilles

1

Örneğiniz, dosyadaki toplam sayısını değil, yalnızca satır başına örnek sayısını yazdırır. İstediğin buysa, böyle bir şey işe yarayabilir:

perl -nle '$c+=scalar(()=m/needle/g);END{print $c}' 

Haklısın - benim örneğim sadece ilk satırdaki olayları sayar.

1

Bu benim saf bash çözümüm

#!/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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.