grep sayısı çoklu oluşumlar


7

Tek bir komutta bir dosyada birden fazla olayın grep sayısını yapmak mümkün müdür? Örneğin:

$ cat > file
blah alfa
beta blah
blah blahgamma
gamma

Yapabilirim:

grep -c 'alfa' file 
1 
grep -c 'beta' file  
1
grep -c 'gamma' file  
2

Ancak böyle bir şey yapmak mümkün mü:

grep -c -e 'alfa' -e 'beta' -e 'gamma' -somemoreblackmagic file

ve her biri için sayıları almak?

alfa 1
beta 1
gamma 2

2
Sen sort file | uniq -cve sonra bunu yapamaz mısın grep?
slhck

@slhck, lütfen cevap olarak gönderin
glenn jackman

@glenn Peki, önce sayıyı ve sonra anahtar kelimeyi vererek aynı çıktı biçimini izlemiyor. OP bunun bir çözüm olduğunu düşünüyorsa, elbette daha ayrıntılı bir cevap vermekten memnuniyet
duyarım

@slhck sadece bir
bornoz

1
Bu cevap, arama dizelerinin tamamlanmış satırlar olacağını (yani, oluştukları herhangi bir satırdaki tek şey) olacağını varsayar. Girilirse alfax, alfay, alfaz(ayrı satırlara), bu yazı bildirir 1 alfax/ 1 alfay/ 1 alfazyerine 3 alfa. Bir giriş satırı alfa beta(aynı satırda) / 1 alfa betayerine bir raporla sonuçlanacaktır . 1 alfa1 beta
Scott

Yanıtlar:


3

Grep'in yapmak istediğini yapabileceğini sanmıyorum.

Sadece bunun yerine awk kullanın :-)

Bu çözüm büyük dosyalar için iyi çalışmayabilir (optimize edilmemiştir). Ve sadece düz kelimeler için çalışır - regexps. Ancak istenirse bazı özellikler eklemek kolaydır.

Kısıtlamaları olan düşük uçlu versiyon, aşağıdaki açıklamalarda belirtilmiştir:

awk '
{
    split($0, b); for (i in b) ++A[b[i]]
}
END {
    split("'"$*"'", a)
    for (i in a) print sprintf("%s %d", a[i], A[a[i]])
}
'

sadece arama dizelerini doğrudan betiğe verin

[EDIT]
regex destekli sabit sürüm (aşağıdaki yoruma bakın). Lütfen hala herhangi bir açık sorun olup olmadığını söyle.

# ---- my favorite ----
awk -F' ?-c ' '
BEGIN { split("'"$*"'", a) }
{ for (i = 2; a[i]; ++i) if (match($0, a[i])) ++A[i] }
END { for (i = 2; a[i]; ++i) if (A[i]) print a[i] " " A[i] }
'
# ---- my favorite ----

örnek kullanım:

script_name -c alfa -c beta -c gamma << !
alfa
beta
gamma
gamma
!

verir:

alfa 1
beta 1
gamma 2

regex kullanımı:

script_name -c   "^al"    -c "beta" -c gamma -c "m.$" << !
alfa
beta
gamma
gamma
!

verir:

^al 1
beta 1
gamma 2
m.$ 2

[/DÜZENLE]


(1) Büyük problem: bu çözüm arama dizgilerinin ayrı kelimeler olacağını varsaymaktadır. Bir giriş satırı olarak alfalfasayılmaz alfa. (2) İlginç özellik: bu çözüm, her satırdaki her oluşumu sayar. Bir giriş satırı, alfa alfa alfaüç oluşum olarak sayılır alfa. Bu arzu edilebilir, ancak grep –csorunun sorduğu şeyle tutarlı değil .
Scott

kesinlikle haklısın. Ben sadece grep'in yaptığı gibi satırlardaki egrep sayma olaylarındaki gibi bir regexp belirtmenize izin veren başka bir çözüm ekledim. Umarım şimdi daha uyumludur :-)
sparkie 18

argüman alıntı hala yine de sorunları olabilir
seyrek

@ sparkie, teşekkürler, regexp olmayan sürümün ortalama dosyalarımda 80-100 saniye sürüyor, bu oldukça iyi.
719016


3

awkKabuk betiği sarmalayıcısı ile birlikte atılmış başka bir çözüm:

#! / bin / sh -
garip
BEGIN {split ("alfa beta gamma", anahtar kelime)
        (anahtar kelimedeki i) için [anahtar sözcük [i]] = 0
}
/ alfa / {count ["alfa"] ++}
/ beta / {count ["beta"] ++}
/ gamma / {count ["gamma"] ++}
SON {
        (i anahtar kelimesindeki) için [i] anahtar kelimesini yazdır, say [anahtar kelimesi [i]]
}'

Çalışma zamanında arama anahtar kelimelerini seçebilmek (ve sparkie'nin cevabında olduğu gibi bunları bağımsız değişken olarak sunmak) istiyorsanız, bu komut awkdosyası komut dosyasını dinamik olarak oluşturmak için uyarlanabilir .


Teşekkürler. Yazılabilirlik, yeniden kullanılabilirlik ve okunabilirlik için +1!
PonyEars

0

Perl çözümü:

perl -lne 'chomp;$s{$_}++ if /alpha|beta|gamma/ }{ print "$_ $s{$_}" for keys %s' file

Bu, grep -cher satırda birden fazla kelime varmış gibi aynı sonucu vermez .
Thor

0

Hiçbir geçiş bir seferde bunu yapamaz, awk kullanmanızı öneririm:

awk -v pat='alfa beta gamma' '
  BEGIN { split(pat, p) } 

  { for(k in p) if($0 ~ p[k]) c[k]++ }

  END { for(k in p) print p[k], c[k]?c[k]:0 }
'

Veya oldukça uzun bir gömlek:

awk -v pat='alfa beta gamma' 'BEGIN { split(pat, p) } { for(k in p) if($0 ~ p[k]) c[k]++ } END { for(k in p) print p[k], c[k]?c[k]:0 }'

açıklama

patpdiziye ayrılır ve ardından her satırdaki eşleşmeleri aramak için kullanılır ( $0 ~ p[k]). Sayaçlar cdizide tutulur . c[k]?c[k]:0Biraz zaman 0 yazdırmak için üçlü operatörü kullanan c[k]sıfırdır.

Desendinizde boşluk varsa pat, splitkomutları buna göre güncellemek için desenler arasında farklı bir sınırlayıcı kullanmanız gerekir .

Test yapmak

Giriş:

cat << EOF > file
alfa
beta
gamma
gamma
EOF

Çıkış pat='alfa beta gamma':

alfa 1
beta 1
gamma 2

Giriş:

cat << EOF > file
alfa beta
beta
gamma gamma
gamma alfa
alfalfa
alfa alfa
EOF

Çıkış pat='^a a$ alfa beta gamma':

beta 2                                          
gamma 2
^a 3
a$ 6
alfa 4

Çıktı her iki durumda da çıkışın grep -cher desenle ayrı ayrı çalışmasını sağlar.


(1) Küçük bir nokta: aksine grep –c, bu 0girdide 0 kez görünen herhangi bir dizgenin oluşumunu rapor etmez. (2) Büyük sorun: bu çözüm arama dizelerinin bir satırdaki ilk kelime olacağını varsayar. Bir giriş satırı olarak foo alfasayılmaz alfa. (3) Başka bir sorun: bu çözüm arama dizelerinin ayrı kelimeler olacağını varsayar. Girilirse alfax, alfay, alfaz(ayrı satırlara), bu yazı bildirir alfax 1/ alfay 1/ alfaz 1yerine alfa 3.
Scott

@Scott: Haklısın, bu işe yaramaz grep -c. Ona başka bir bıçak bulacağım.
Thor

0

uniq(İle sort) kullanmanızı öneririm .

$ sort file | uniq -c
1 alfa
1 beta
2 gamma

İhtiyacınız sortdosya sıralanabilir olmayabilir if (aslında, birden oluşumları ardışık hatlarda olmayabilir ise).

GÜNCELLEŞTİRME:

Önceden tanımlanmış kalıplara sahip olduğunuzu ve boşluk içermediğini varsayarsak:

$ PATTERNS='alfa beta gamma'

$ for P in $PATTERNS; do echo $P `grep -c $P file`; done
alfa 1
beta 1
gamma 2

Teşekkürler Bunu düşündüm ama grep kelimeleri her zaman satırın başında değil.
719016

Tamam, ama şimdi sorunuzu farklı bir şeye değiştirdiniz. Artık verilen açık bir liste olduğunu ima ediyorsunuz. Cevabımı da güncelledim, sonra yukarıya bakın.
Daniel,

0

İşte günlük işlerimden bir örnek:

FlowBase.java ile biten Tüm Dosyalar, "Girdi *"> 1 dizesinin oluşması

Örnek: dosya listelenecek

"İnputABD"

"İnputABD"

$ i için $ (find. | grep FlowBase.java); $ i $ (egrep "girişi. " $ i | sed 's / ^. "girişini yapın. // giriş | $ 2} 'yazdır | wc -l); bitti | awk '($ 2> 0) {$ $} yazdır

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.