จะนับข้อความในไฟล์ได้อย่างไร?


19

ฉันมีไฟล์บันทึกเรียงตามที่อยู่ IP ฉันต้องการค้นหาจำนวนครั้งของที่อยู่ IP ที่ไม่ซ้ำกันแต่ละรายการ ฉันจะทำสิ่งนี้ด้วยการทุบตีได้อย่างไร อาจแสดงรายการจำนวนครั้งที่อยู่ถัดจาก 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
ด้วย“ bash” คุณหมายถึงเชลล์ธรรมดาหรือบรรทัดคำสั่งทั่วไปหรือไม่
ของหวาน

1
คุณมีซอฟต์แวร์ฐานข้อมูลที่พร้อมใช้งานหรือไม่
SpacePhoenix


บันทึกมาจากเซิร์ฟเวอร์ appache2 ไม่ใช่ฐานข้อมูลจริงๆ ทุบตีเป็นสิ่งที่ฉันต้องการในกรณีการใช้งานทั่วไป ฉันเห็นโซลูชันของ python และ perl หากพวกเขาดีต่อคนอื่นมันยอดเยี่ยมมาก การเรียงลำดับเริ่มต้นทำได้ด้วยsort -Vแม้ว่าฉันคิดว่าไม่จำเป็น ฉันส่งผู้ละเมิด 10 อันดับแรกของหน้าเข้าสู่ระบบไปยังผู้ดูแลระบบพร้อมคำแนะนำสำหรับการห้ามซับเน็ตที่เกี่ยวข้อง ตัวอย่างเช่นหนึ่ง IP เข้าสู่หน้าเข้าสู่ระบบมากกว่า 9000 ครั้ง IP และซับเน็ตคลาส D นั้นอยู่ในรายการที่ไม่อนุญาต ฉันแน่ใจว่าเราสามารถทำให้สิ่งนี้เป็นแบบอัตโนมัติได้ แต่นั่นเป็นคำถามที่แตกต่าง
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ลบบรรทัดที่ซ้ำแล้วจึงทำให้คุณมีรายการที่อยู่ IP ด้วยการแทนที่คำสั่งforลูปจะวนรอบรายการนี้เพื่อพิมพ์ IP ที่ประมวลผลในปัจจุบันตามด้วย“ count” และ count หลังถูกคำนวณโดย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
โซลูชันนี้วนซ้ำไฟล์อินพุตซ้ำหนึ่งครั้งสำหรับแต่ละที่อยู่ IP ซึ่งจะช้ามากหากไฟล์มีขนาดใหญ่ โซลูชันอื่น ๆ ที่ใช้uniq -cหรือawkจำเป็นต้องอ่านไฟล์เพียงครั้งเดียวเท่านั้น
David

1
@ David นี่เป็นความจริง แต่นี่จะเป็นครั้งแรกของฉันที่จะได้รับมันเช่นกันรู้ว่า grep นับ หากประสิทธิภาพไม่สามารถวัดได้ว่าเป็นปัญหา ... อย่าเพิ่มประสิทธิภาพก่อนเวลาอันควร
D. Ben Knoble

3
ฉันจะไม่เรียกมันว่าการเพิ่มประสิทธิภาพก่อนวัยอันควรเนื่องจากโซลูชันที่มีประสิทธิภาพมากขึ้นนั้นก็ง่ายกว่า
David

โดยวิธีการทำไมมันเขียนเป็น<log grep ...และไม่grep ... log?
ซันติอาโก

@Santiago เพราะเห็นว่าดีกว่าในหลาย ๆ ด้านเป็นStéphane Chazelas อธิบายที่นี่ใน U & L
ของหวาน

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 .... ในกรณีที่คุณไม่แน่ใจว่าไฟล์ถูกเรียงลำดับแล้ว
Guntram Blohm สนับสนุน Monica

14

หากคุณไม่ต้องการรูปแบบการแสดงผลที่เฉพาะเจาะจงฉันขอแนะนำคำตอบที่โพสต์cut+ uniqตามแล้ว

หากคุณต้องการรูปแบบเอาต์พุตที่ให้มาจริงๆวิธีเดียวในการทำใน Awk ก็คือ

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

นี้จะค่อนข้างที่ไม่เหมาะอย่างยิ่งเมื่อมีการป้อนข้อมูลจะถูกจัดเรียงอยู่แล้วเพราะมันไม่จำเป็น IP ที่ร้านค้าทั้งหมดลงในหน่วยความจำ - ดี แต่มีความซับซ้อนมากขึ้นวิธีที่จะทำในกรณีที่ก่อนเรียง (เพิ่มเติมเทียบเท่าโดยตรง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

มันจะง่ายต่อการเปลี่ยนคำตอบที่ตัด + uniq ด้วย sed เพื่อให้ปรากฏในรูปแบบที่ต้องการ
ปีเตอร์ - คืนสิทธิ์ให้กับโมนิกา

@ PeterA.Schneider ใช่มันจะ - ฉันเชื่อว่ามีอยู่แล้วชี้ให้เห็นในความคิดเห็นต่อคำตอบนั้น
steeldriver


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...
D. เบน Knoble

1
ซึ่งหมายความว่าคุณจะต้องดำเนินการทั้งไฟล์หลายครั้ง หนึ่งครั้งเพื่อรับรายการ IP จากนั้นอีกครั้งสำหรับแต่ละ IP ที่คุณพบ
terdon

5

บาง Perl:

$ 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

นี่เป็นแนวคิดเดียวกันกับวิธี awk ของ Steeldriverแต่ใน Perl -aสาเหตุ Perl จะแยกสายการป้อนข้อมูลแต่ละโดยอัตโนมัติลงในอาร์เรย์@Fที่มีองค์ประกอบแรก (ไอพี) $F[0]เป็น ดังนั้น$k{$F[0]}++จะสร้างแฮช%kซึ่งคีย์คือ IP และค่าที่เป็นจำนวนครั้งที่แต่ละ IP เห็น }{เป็น perlspeak ขี้ขลาดสำหรับ "ทำส่วนที่เหลือที่ส่วนท้ายสุดหลังจากการประมวลผลอินพุตทุกคน" ดังนั้นในตอนท้ายสคริปต์จะวนซ้ำปุ่มของแฮชและพิมพ์คีย์ปัจจุบัน ( $_) พร้อมกับค่า ( $k{$_})

และเพื่อให้ผู้คนไม่คิดว่า perl บังคับให้คุณเขียนสคริปต์ที่ดูเหมือน scribblings ที่คลุมเครือนี่คือสิ่งเดียวกันในรูปแบบย่อน้อย:

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 Nเปรียบเทียบไม่เกินNตัวอักษรในบรรทัด

-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 อาจจะตัวละครอย่างต่อเนื่องหลังจากที่ IP `- - [` แต่ในทางทฤษฎีที่อยู่อาจมีความยาวน้อยกว่าสูงสุด 8 ตัวอักษรดังนั้นการเปลี่ยนแปลงวันที่อาจแบ่งการนับสำหรับ IP ดังกล่าว และอย่างที่คุณบอกไว้สิ่งนี้จะไม่ทำงานสำหรับ IPv6
Martin Thornton

ฉันชอบมันฉันไม่ทราบว่า uniq สามารถนับได้!
j0h

1

FWIW, Python 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บอกให้นับเหตุการณ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.