किसी फ़ाइल में पाठ की घटनाओं को कैसे गिना जाए?


19

मेरे पास IP पते द्वारा सॉर्ट की गई एक फ़ाइल है, मैं प्रत्येक अद्वितीय आईपी पते की घटनाओं की संख्या का पता लगाना चाहता हूं। मैं इसे बैश के साथ कैसे कर सकता हूं? संभवतः एक आईपी के बगल में होने वाली घटनाओं की संख्या को सूचीबद्ध करना, जैसे:

5.135.134.16 count: 5
13.57.220.172: count 30
18.206.226 count:2

और इसी तरह।

यहाँ लॉग का एक नमूना है:

5.135.134.16 - - [23/Mar/2019:08:42:54 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:55 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:55 -0400] "POST /wp-login.php HTTP/1.1" 200 3836 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:55 -0400] "POST /wp-login.php HTTP/1.1" 200 3988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
5.135.134.16 - - [23/Mar/2019:08:42:56 -0400] "POST /xmlrpc.php HTTP/1.1" 200 413 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:05 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:06 -0400] "POST /wp-login.php HTTP/1.1" 200 3985 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:07 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:08 -0400] "POST /wp-login.php HTTP/1.1" 200 3833 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:09 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:11 -0400] "POST /wp-login.php HTTP/1.1" 200 3836 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:12 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:15 -0400] "POST /wp-login.php HTTP/1.1" 200 3837 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.220.172 - - [23/Mar/2019:11:01:17 -0400] "POST /xmlrpc.php HTTP/1.1" 200 413 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
13.57.233.99 - - [23/Mar/2019:04:17:45 -0400] "GET / HTTP/1.1" 200 25160 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_12_6) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/61.0.3163.100 Safari/537.36"
18.206.226.75 - - [23/Mar/2019:21:58:07 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "https://www.google.com/url?3a622303df89920683e4421b2cf28977" "Mozilla/5.0 (Windows NT 6.2; rv:33.0) Gecko/20100101 Firefox/33.0"
18.206.226.75 - - [23/Mar/2019:21:58:07 -0400] "POST /wp-login.php HTTP/1.1" 200 3988 "https://www.google.com/url?3a622303df89920683e4421b2cf28977" "Mozilla/5.0 (Windows NT 6.2; rv:33.0) Gecko/20100101 Firefox/33.0"
18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"
18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] "GET /wp-login.php HTTP/1.1" 200 2988 "-" "Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:62.0) Gecko/20100101 Firefox/62.0"

1
"बैश" के साथ, क्या आपका मतलब सादे खोल या सामान्य रूप से कमांड लाइन है?
मिठाई

1
क्या आपके पास उपयोग करने के लिए कोई डेटाबेस सॉफ्टवेयर उपलब्ध है?
SpacePhoenix


लॉग एक appache2 सर्वर से है, वास्तव में एक डेटाबेस नहीं है। बैश वह है जो मैं पसंद करूंगा, एक सामान्य उपयोग के मामले में। मैं अजगर और पर्ल समाधान देखता हूं, अगर वे किसी और के लिए अच्छे हैं, तो यह बहुत अच्छा है। प्रारंभिक छँटाई के साथ किया गया था, sort -Vहालांकि मुझे लगता है कि आवश्यक नहीं था। मैंने संबंधित सबनेट पर प्रतिबंध लगाने की सिफारिशों के साथ लॉगिन पृष्ठ के शीर्ष 10 एब्सर्स को सिस्टम एडमिन को भेज दिया। उदाहरण के लिए, एक आईपी ने लॉगिन पृष्ठ को 9000 से अधिक बार मारा। उस IP, और उसके वर्ग डी सबनेट को अब ब्लैकलिस्ट कर दिया गया है। मुझे यकीन है कि हम इसे स्वचालित कर सकते हैं, हालांकि यह एक अलग सवाल है।
j0h

जवाबों:


13

आप उपयोग कर सकते हैं grepऔर uniqउन पर पतों की सूची के लिए, पाश और grepगिनती के लिए फिर से:

for i in $(<log grep -o '^[^ ]*' | uniq); do
  printf '%s count %d\n' "$i" $(<log grep -c "$i")
done

grep -o '^[^ ]*'प्रत्येक वर्ण को शुरुआत से ( ^) प्रत्येक पंक्ति के पहले स्थान तक आउटपुट uniqदेता है , दोहराया लाइनों को हटाता है, इस प्रकार आपको आईपी पते की सूची के साथ छोड़ देता है। कमांड प्रतिस्थापन के लिए धन्यवाद, forइस सूची पर लूप लूप करता है जो वर्तमान में संसाधित आईपी को "गिनती" और गिनती के बाद प्रिंट करता है। उत्तरार्द्ध द्वारा गणना की जाती है grep -c, जो कम से कम एक मैच के साथ लाइनों की संख्या को गिनता है।

उदाहरण चलाते हैं

$ for i in $(<log grep -o '^[^ ]*'|uniq);do printf '%s count %d\n' "$i" $(<log grep -c "$i");done
5.135.134.16 count 5
13.57.220.172 count 9
13.57.233.99 count 1
18.206.226.75 count 2
18.213.10.181 count 3

13
यह समाधान प्रत्येक आईपी पते के लिए, बार-बार इनपुट फ़ाइल पर पुनरावृत्ति करता है, जो फ़ाइल बड़ी होने पर बहुत धीमी हो जाएगी। उपयोग करने वाले अन्य समाधानों uniq -cया awkकेवल एक बार फाइल को पढ़ने की आवश्यकता है,
डेविड

1
@ यह सच है, लेकिन यह मेरा पहला कदम होगा, यह जानते हुए भी कि grep मायने रखता है। जब तक प्रदर्शन औसतन एक समस्या नहीं है ... न ही समय से पहले अनुकूलन?
डी। बेन नोबल

3
मैं इसे समयपूर्व अनुकूलन नहीं कहूंगा, यह देखते हुए कि अधिक कुशल समाधान भी सरल है, लेकिन प्रत्येक अपने स्वयं के लिए।
डेविड

वैसे, इसे क्यों <log grep ...और grep ... logकैसे लिखा जाता है ?
सैंटियागो

@ सैंटियागो क्योंकि यह कई मायनों में बेहतर है, क्योंकि स्टीफन चेज़लस यहां यू एंड एल पर बताते हैं
मिठाई

39

आप उपयोग कर सकते हैं cutऔर uniqउपकरण:

cut -d ' ' -f1 test.txt  | uniq -c
      5 5.135.134.16
      9 13.57.220.172
      1 13.57.233.99
      2 18.206.226.75
      3 18.213.10.181

स्पष्टीकरण:

  • cut -d ' ' -f1 : पहला फ़ील्ड निकालें (IP पता)
  • uniq -c : दोहराया लाइनों की रिपोर्ट करें और घटनाओं की संख्या प्रदर्शित करें

6
एक का उपयोग कर सकते हैं sed, उदाहरण के sed -E 's/ *(\S*) *(\S*)/\2 count: \1/'लिए ओपी चाहता था जैसे उत्पादन प्राप्त करने के लिए।
मिठाई

2
यह स्वीकृत उत्तर होना चाहिए, क्योंकि मिठाई वाले को फ़ाइल को बार-बार पढ़ने की आवश्यकता होती है इसलिए यह बहुत धीमी है। और आप आसानी से उपयोग कर सकते हैं sort file | cut .... यदि फ़ाइल पहले से ही छँटी हुई है तो आप निश्चित नहीं हैं।
गुंतराम ब्लोहम

14

यदि आपको विशेष रूप से दिए गए आउटपुट प्रारूप की आवश्यकता नहीं है, तो मैं पहले से ही पोस्ट किए गए + आधारित उत्तर की सिफारिश करूंगाcutuniq

यदि आपको वास्तव में दिए गए आउटपुट प्रारूप की आवश्यकता है, तो इसे Awk में करने का एकल-पास तरीका होगा

awk '{c[$1]++} END{for(i in c) print i, "count: " c[i]}' log

यह कुछ गैर-आदर्श है जब इनपुट पहले से ही सॉर्ट किया जाता है क्योंकि यह अनावश्यक रूप से सभी आईपी को मेमोरी में संग्रहीत करता है - एक बेहतर, हालांकि अधिक जटिल, इसे पूर्व-सॉर्ट किए गए मामले में करने का तरीका (अधिक सीधे समकक्ष uniq -c):

awk '
  NR==1 {last=$1} 
  $1 != last {print last, "count: " c[last]; last = $1} 
  {c[$1]++} 
  END {print last, "count: " c[last]}
'

पूर्व।

$ awk 'NR==1 {last=$1} $1 != last {print last, "count: " c[last]; last = $1} {c[$1]++} END{print last, "count: " c[last]}' log
5.135.134.16 count: 5
13.57.220.172 count: 9
13.57.233.99 count: 1
18.206.226.75 count: 2
18.213.10.181 count: 3

मांग वाले प्रारूप में प्रदर्शित होने के लिए sed के साथ कट + यूनीक आधारित उत्तर को बदलना आसान होगा।
पीटर -

@ PeterA.Schneider हाँ, यह होगा - मेरा मानना ​​है कि पहले से ही उस जवाब के लिए टिप्पणियों में बताया गया था
स्टीलराइवर

आह, हाँ, मैं देख रहा हूँ।
पीटर -

8

यहाँ एक संभव समाधान है:

IN_FILE="file.log"
for IP in $(awk '{print $1}' "$IN_FILE" | sort -u)
do
    echo -en "${IP}\tcount: "
    grep -c "$IP" "$IN_FILE"
done
  • file.logवास्तविक फ़ाइल नाम के साथ बदलें ।
  • कमांड प्रतिस्थापन अभिव्यक्ति $(awk '{print $1}' "$IN_FILE" | sort -u)पहले कॉलम के अनूठे मूल्यों की एक सूची प्रदान करेगी।
  • फिर grep -cफ़ाइल के भीतर इनमें से प्रत्येक मान को गिना जाएगा।

$ IN_FILE="file.log"; for IP in $(awk '{print $1}' "$IN_FILE" | sort -u); do echo -en "${IP}\tcount: "; grep -c "$IP" "$IN_FILE"; done
13.57.220.172   count: 9
13.57.233.99    count: 1
18.206.226.75   count: 2
18.213.10.181   count: 3
5.135.134.16    count: 5

1
पसंद करें printf...
डी। बेन नोबल

1
इसका मतलब है कि आपको पूरी फ़ाइल को कई बार संसाधित करने की आवश्यकता है। एक बार आईपी की सूची प्राप्त करने के लिए और फिर एक बार आपके द्वारा ढूंढे गए प्रत्येक आईपी के लिए।
टेराडन

5

कुछ पर्ल:

$ perl -lae '$k{$F[0]}++; }{ print "$_ count: $k{$_}" for keys(%k)' log 
13.57.233.99 count: 1
18.206.226.75 count: 2
13.57.220.172 count: 9
5.135.134.16 count: 5
18.213.10.181 count: 3

यह स्टीलड्राइवर के अजीब दृष्टिकोण के समान है , लेकिन पर्ल में। -aकारणों में स्वचालित रूप से सरणी में प्रत्येक इनपुट लाइन विभाजित करने के लिए पर्ल @F, जिसका पहला तत्व (आईपी) है $F[0]। तो, $k{$F[0]}++हैश बनाएगा %k, जिसकी कुंजी आईपी हैं और जिनके मूल्य प्रत्येक आईपी को देखे जाने की संख्या है। }{"सभी इनपुट संसाधित करने के बाद अंत में आराम करते हैं," के लिए फैशनेबल perlspeak है। तो, अंत में, स्क्रिप्ट हैश की कुंजियों पर पुनरावृति करेगी और वर्तमान कुंजी ( $_) को उसके मूल्य ( $k{$_}) के साथ प्रिंट करेगी ।

और, बस लोगों को यह नहीं लगता कि पर्ल आपको स्क्रिप्ट लिखने के लिए मजबूर करता है जो कि क्रिप्टिक स्क्रिबब्लिंग्स की तरह दिखता है, यह कम संघनित रूप में एक ही बात है:

perl -e '
  while (my $line=<STDIN>){
    @fields = split(/ /, $line);
    $ip = $fields[0];
    $counts{$ip}++;
  }
  foreach $ip (keys(%counts)){
    print "$ip count: $counts{$ip}\n"
  }' < log

4

शायद यह नहीं है कि ओपी क्या चाहता है; हालाँकि, अगर हम जानते हैं कि IP पता लंबाई 15 वर्णों तक सीमित होगी, तो एक विशाल लॉग फ़ाइल से अद्वितीय IP के साथ काउंट प्रदर्शित करने का एक तेज़ तरीका uniqअकेले कमांड का उपयोग करके प्राप्त किया जा सकता है :

$ uniq -w 15 -c log

5 5.135.134.16 - - [23/Mar/2019:08:42:54 -0400] ...
9 13.57.220.172 - - [23/Mar/2019:11:01:05 -0400] ...
1 13.57.233.99 - - [23/Mar/2019:04:17:45 -0400] ...
2 18.206.226.75 - - [23/Mar/2019:21:58:07 -0400] ...
3 18.213.10.181 - - [23/Mar/2019:14:45:42 -0400] ...

विकल्प:

-w NNलाइनों में वर्णों की तुलना में अधिक नहीं

-c घटनाओं की संख्या के आधार पर उपसर्ग लाइनें होगी

वैकल्पिक रूप से, सटीक स्वरूपित आउटपुट के लिए मैं पसंद करता हूं awk(IPV6 पतों के लिए भी काम करना चाहिए), ymmv।

$ awk 'NF { print $1 }' log | sort -h | uniq -c | awk '{printf "%s count: %d\n", $2,$1 }'

5.135.134.16 count: 5
13.57.220.172 count: 9
13.57.233.99 count: 1
18.206.226.75 count: 2
18.213.10.181 count: 3

ध्यान दें कि uniqयदि वे आसन्न नहीं हैं, तो इनपुट फ़ाइल में बार-बार आने वाली लाइनों का पता नहीं लगाया जाएगा, इसलिए यह sortफ़ाइल के लिए आवश्यक हो सकता है ।


1
अभ्यास में काफी अच्छा है, लेकिन कोने के मामलों पर ध्यान देने योग्य है। आईपी ​​के बाद केवल 6 शायद निरंतर अक्षर - - [`। लेकिन सिद्धांत रूप में पता अधिकतम 8 वर्णों से छोटा हो सकता है, इसलिए तारीख का परिवर्तन ऐसे आईपी के लिए गिनती को विभाजित कर सकता है। और जैसा कि आप संकेत देते हैं, यह आईपीवी 6 के लिए काम नहीं करेगा।
मार्टिन थॉर्नटन

मुझे यह पसंद है, मुझे नहीं पता था कि uniq गिनती कर सकता है!
j0h 12

1

FWIW, पायथन 3:

from collections import Counter

with open('sample.log') as file:
    counts = Counter(line.split()[0] for line in file)

for ip_address, count in counts.items():
    print('%-15s  count: %d' % (ip_address, count))

आउटपुट:

13.57.233.99     count: 1
18.213.10.181    count: 3
5.135.134.16     count: 5
18.206.226.75    count: 2
13.57.220.172    count: 9

0
cut -f1 -d- my.log | sort | uniq -c

स्पष्टीकरण: my.log का पहला क्षेत्र डैश पर विभाजित करें -और इसे सॉर्ट करें। uniqइनपुट की जरूरत है। -cइसे घटनाओं को गिनने के लिए कहता है।

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