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.