Grep के उपयोग से होने वाली घटनाओं की कुल संख्या की गणना करें


215

grep -cयह पता लगाने के लिए उपयोगी है कि किसी फ़ाइल में स्ट्रिंग कितनी बार होती है, लेकिन यह केवल प्रत्येक घटना को प्रति पंक्ति एक बार गिना जाता है। प्रति पंक्ति कई आवृत्तियों की गणना कैसे करें?

मैं कुछ और अधिक सुंदर की तलाश में हूँ:

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

4
मुझे पता grepहै कि निर्दिष्ट है, लेकिन किसी का उपयोग करने के लिए ack, जवाब बस है ack -ch <pattern>
काइल स्ट्रैंड

जवाबों:


302

जीआरईपी -oकेवल मैचों का उत्पादन करेगा, लाइनों की अनदेखी करेगा; wcउन्हें गिन सकते हैं:

grep -o 'needle' file | wc -l

यह will सुइयों ’या also मल्टीलाइन’ से भी मेल खाएगा।
केवल एकल शब्द:

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

6
ध्यान दें कि इसके लिए GNU grep (Linux, Cygwin, FreeBSD, OSX) की आवश्यकता होती है।
गिल्स

@wag क्या जादू करता है \bऔर \Bयहाँ क्या?
गीक

6
@ गीक \ b एक शब्द सीमा से मेल खाता है, \ B एक शब्द सीमा से मेल नहीं खाता है। उपरोक्त उत्तर अधिक सही होगा यदि यह दोनों सिरों पर \ b का उपयोग करता है।
लियाम

1
प्रति पंक्ति घटनाओं की गणना के लिए, grep -n विकल्प और uniq -c के साथ संयोजन करें ... grep -no '\ <सुई \>' फ़ाइल | uniq -c
jameswarren 13

@jameswarren uniqकेवल आसन्न समान रेखाओं को हटाता है, आपको sortखिलाने से uniqपहले यह सुनिश्चित करने की आवश्यकता है कि क्या आप पहले से ही सुनिश्चित नहीं हैं कि डुप्लिकेट हमेशा निकटवर्ती होगा।
ट्रिपलआई

16

आप जीएनयू ग्रेप (हमेशा लिनक्स और Cygwin, कभी कभी कहीं पर) है, तो आप कर सकते हैं से उत्पादन लाइनों गिनतीgrep -o : grep -o needle | wc -l

पर्ल के साथ, यहां कुछ तरीके हैं जो मुझे आपकी तुलना में अधिक सुरुचिपूर्ण लगते हैं (भले ही यह तय हो जाने के बाद )।

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'

केवल पोसिक्स टूल के साथ, एक दृष्टिकोण, यदि संभव हो तो इनपुट को एक मैच के साथ लाइनों में विभाजित करने के लिए इसे grep करने से पहले विभाजित करना है। उदाहरण के लिए, यदि आप पूरे शब्दों की तलाश कर रहे हैं, तो पहले हर गैर-शब्द चरित्र को एक नई पंक्ति में बदल दें।

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

अन्यथा, टेक्स्ट प्रोसेसिंग के इस विशेष बिट को करने के लिए कोई मानक कमांड नहीं है, इसलिए आपको sed (यदि आप एक मसोकिस्ट हैं) या 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

यहाँ का उपयोग कर एक सरल समाधान है sedऔर grepहै, जो तार के लिए या नियमित अभिव्यक्ति भी दर-पुस्तक काम करता है लेकिन लंगर पैटर्न के साथ कुछ कोने मामलों में विफल रहता है (के दो घटनाओं यह जैसे पाता है ^needleया \bneedleमें needleneedle)।

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

ध्यान दें कि ऊपर दिए गए sed प्रतिस्थापन में, मैं \nएक नई पंक्ति का मतलब करता था। यह पैटर्न भाग में मानक है, लेकिन प्रतिस्थापन पाठ में, पोर्टेबिलिटी के लिए, बैकस्लैश-न्यूलाइन के लिए स्थानापन्न \n


4

अगर, मेरी तरह, आप वास्तव में "दोनों; प्रत्येक बिल्कुल एक बार" चाहते थे , (यह वास्तव में "या तो दो बार है") तो यह आसान है;

grep -E "thing1|thing2" -c

और आउटपुट के लिए जाँच करें 2

इस दृष्टिकोण का लाभ (अगर ठीक एक बार है कि आप क्या चाहते) यह आसानी से मापता है।


मुझे यकीन नहीं है कि आप वास्तव में जाँच रहे हैं कि यह केवल एक बार दिखाई दे रहा है? आप सभी के लिए देख रहे हैं कि या तो उन शब्दों में से एक कम से कम एक बार मौजूद है।
स्टीव गोर

3

Awk और needleफ़ील्ड सेपरेटर के रूप में एक और उपाय :

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

यदि आप needleविराम चिह्नों के साथ मिलान करना चाहते हैं , तो फ़ील्ड विभाजक को तदनुसार बदलें

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

या कक्षा का उपयोग करें: [^[:alnum:]]सभी गैर अल्फा वर्णों को शामिल करने के लिए।


ध्यान दें कि इसके लिए एक awk की आवश्यकता होती है जो regexp फ़ील्ड विभाजकों (जैसे GNU awk) का समर्थन करता है।
गिल्स

1

आपका उदाहरण केवल प्रति-पंक्ति घटनाओं की संख्या को प्रिंट करता है, और फ़ाइल में कुल नहीं। यदि आप यही चाहते हैं, तो कुछ इस तरह काम कर सकता है:

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

आप सही हैं - मेरा उदाहरण केवल पहली पंक्ति में होने वाली घटनाओं को गिनता है।

1

यह मेरा शुद्ध बैश समाधान है

#!/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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.