파일에서 텍스트 발생 횟수를 계산하는 방법은 무엇입니까?


19

IP 주소별로 정렬 된 로그 파일이 있으며 각 고유 IP 주소의 발생 횟수를 찾고 싶습니다. bash로 어떻게 할 수 있습니까? 다음과 같이 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 서버에서 가져온 것입니다. bash는 일반적인 사용 사례에서 선호하는 것입니다. 파이썬과 펄 솔루션이 다른 사람에게 좋으면 훌륭하다는 것을 알았습니다. 초기 정렬은 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"및 카운트를 인쇄합니다. 후자는로 계산되며 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
더 효율적인 솔루션이 더 단순하지만 각각 자체적으로 제공한다는 점을 감안할 때 조기 최적화라고 부를 수는 없습니다.
데이비드

그런데 왜 그렇게 쓰여 <log grep ...있지 grep ... log않습니까?
산티아고

@Santiago Stéphane Chazelas가 U & L에 대해 설명하는 것처럼 여러면에서 더 좋습니다 .
디저트

39

당신은 사용할 수 있습니다 cutuniq도구 :

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
sedsed -E 's/ *(\S*) *(\S*)/\2 count: \1/'를 들어 OP가 원하는 것과 정확히 같은 결과를 얻기 위해을 사용할 수 있습니다 .
디저트

2
디저트가 파일을 반복적으로 읽을 필요가 있기 때문에 허용되는 답변이어야합니다. sort file | cut .... 파일이 이미 정렬되어 있는지 확실하지 않은 경우 쉽게 사용할 수 있습니다 .
Guntram Blohm은

14

주어진 출력 형식이 특별히 필요하지 않은 경우 이미 게시 된 + 기반 답변을 권장합니다cutuniq

주어진 출력 형식 이 실제로 필요한 경우 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

sed를 사용하여 cut + uniq 기반 답변을 원하는 형식으로 쉽게 변경할 수 있습니다.
피터-11

@ 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...
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

이것은 Steeldriver의 awk 접근 방식 과 동일하지만 Perl 에서는 동일 합니다. -a원인 자동 배열로 각각의 입력 라인을 분할 펄 @F모국어 소자합니다 (IP)이다 $F[0]. 따라서 키가 IP이고 값이 각 IP를 본 횟수 인 $k{$F[0]}++hash를 생성합니다 %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

아마도 이것은 OP가 원하는 것이 아닐 수도 있습니다. 그러나 IP 주소 길이가 15 자로 제한된다는 것을 알고 있다면 uniq명령 만 사용하여 큰 로그 파일에서 고유 한 IP로 카운트를 표시하는 더 빠른 방법을 얻을 수 있습니다 .

$ 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
실제로는 충분하지만 코너 사례를 주목할 가치가 있습니다. IP` --[`뒤에는 6 개의 상수 문자 만 있을 수 있습니다. 그러나 이론적으로 주소는 최대 8 자보다 짧을 수 있으므로 날짜를 변경하면 해당 IP의 수를 분할 할 수 있습니다. 그리고 암시 하듯이 IPv6에서는 작동하지 않습니다.
마틴 손튼

나는 그것을 좋아한다, 나는 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.