텍스트, IPv4 및 IPv6 IP, 줄 바꿈, IP 범위 등이 혼합되어 있는 거대한 네트워크 생성 목록에서 IP를 추출하려고 합니다.
다음은 목록의 일부입니다.
; Spamhaus DROP List 2016/07/03 - (c) 2016 The Spamhaus Project
; http://www.spamhaus.org/drop/drop.txt
; Last-Modified: Sun, 3 Jul 2016 21:18:32 GMT
; Expires: Sun, 03 Jul 2016 23:26:45 GMT
1.0.1.0/24
223.223.176.0
129.130.100.100
1.160.118.30
91.121.120.228 # 2016-07-05, ns350944.ip-91-121-120.eu, FRA, 1
62.210.111.59 # 2016-07-05, sender9p2.offresduweb.fr, FRA, 1
52.90.253.169 # 2016-07-05, ec2-52-90-253-169.compute-1.amazonaws.com, USA, 13
2a01:4f8:200:2153::2 # 2016-06-27, 2a01:4f8:200:2153::2, DEU, 2
2601:1c1:8801:618c:9864:3f33:7569:38c4 # 2016-06-28, 2601:1c1:8801:618c:9864:3f33:7569:38c4, USA, 2
#last updated 2016.07.04 1733 UTC
1.0.1.0/24 China
1.0.2.0/23 China
1.0.8.0/21 China
1.0.32.0/19 China
1.1.0.0/24 China
더 큰 조각을 보려면 다음을 참조하세요.페이스트빈그런데 실제 목록에는 44,000개가 넘는 행이 있으므로 이는 완전한 목록이 아닙니다.
내가 하고 싶은 일은 목록에서 일반 IP(IPv4)만 가져오는 것입니다.
위의 내용에 대한 나의 이의는 다음과 같습니다.
grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}'
그것은 나에게 다음을 제공합니다:
1.0.1.0
223.223.176.0
129.130.100.100
1.160.118.3
198.55.103.144
etc
etc
이제 괜찮습니다. 하지만 ".0"(예: 1.0.1.0 또는 223.223.176.0)으로 끝나는 항목은 IP 범위이지 실제 IP가 아니기 때문에 원하지 않습니다. 그래서 위 grep의 출력을 awk 문으로 파이프하여 0으로 끝나는 모든 IP를 제거했습니다.
이 grep (IPs) | awk (remove those that end in 0)
솔루션은 효과가 있지만 이를 수행하고 여러 파이프라인 grep(또는 sed/awk)의 사용을 최소화하는 더 좋은 방법이 있는지 알고 싶습니다.
답변1
awk를 사용하여 모든 작업을 수행할 수 있습니다(물론 경로 이름을 가정).
#!/usr/bin/awk -f
/^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[1-9][0-9]*$/ {
print;
next;
}
/^[0-9][0-9]*\.[0-9][0-9]*\.[0-9][0-9]*\.[1-9][0-9]*[^0-9\.:].*$/ {
sub("[^0-9.].*$","");
print;
}
첫 번째 패턴은 IPv4에만 일치하고(다음 텍스트 없음) 두 번째 패턴은 다른 텍스트 일치를 허용합니다(콜론이 있는 줄은 제외).
그런데 스키마는 다음을 사용하여 고정되어야 합니다."^"
그리고"$"
불필요한 경기를 건너뛰세요.
이는 스크립트로 나타나며 다른 명령(예: grep이 포함된 파이프)처럼 실행될 수 있습니다.
./foo <foo.in
주어진
129.130.100.100
1.160.118.30
91.121.120.228
62.210.111.59
52.90.253.169
IP 주소 뒤의 길 잃은 텍스트 처리를 단순화하기 위해 일치 항목을 두 개의 표현식으로 분할했습니다. 이 범위는 [^0-9:\.:]
적어도 하나의 길 잃은 문자를 처리해야 함을 보장합니다.
awk 프로그램은 스크립트일 필요는 없지만 자유 형식이어야 합니다(단일 명령 문자열을 만들 때 줄 바꿈을 삭제할 수 있음). 그러나 한 줄 결과는 읽기가 어렵습니다.
-o
options grep -E
또는 -E
options 사용 제안과 달리 sed
이 awk
솔루션은 모든 POSIX 시스템에서 작동합니다.
참고용(POSIX):
답변2
0
정규식 끝에 s를 추가 할 수 없도록 지정하세요 .
$ grep -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[1-9][0-9]*' file
129.130.100.100
1.160.118.30
91.121.120.228
62.210.111.59
52.90.253.169
요령은 \.[1-9][0-9]*
a 를 일치시킨 다음 0보다 큰 숫자( 또는 유사한 숫자로 끝나는 IP를 .
가질 수 없음)를 한 번 일치시킨 다음 0에서 9 사이의 0개 이상의 숫자를 일치시키는 것을 의미합니다.019
grep -E
또한 구문을 단순화하기 위해 이를 사용합니다 .
grep -Eo '[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[1-9][0-9]*' file
또는 더 간단하게:
grep -Eo '([0-9]{1,3}\.){3}[1-9]\d*' file
그리고 당신이 grep
그것을 지지한다면, grep -P
그것을 더욱 단순화하십시오:
grep -Po '(\d{1,3}\.){3}[1-9]\d*' file
답변3
$ sed -E -e 's/[[:space:];#\/].*//;
/\.0$+|[0-9a-f]{1,4}:|^[[:space:]]*$/d' spamhaus.txt
129.130.100.100
1.160.118.30
91.121.120.228
62.210.111.59
52.90.253.169
(더 나은 가독성을 위해 줄 바꿈 및 들여쓰기를 추가했습니다.)
- 줄의 첫 번째 공백에 있는 주석과 모든 항목을 제거합니다(즉, 빈 문자열로 바꿉니다).
- 다음을 포함하는 줄을 삭제합니다.
.0
슬래시 또는 줄 끝이 뒤따릅니다.- 1~4자리 16진수 뒤에 ":" 표시
- 빈 줄과 빈 줄만
- 다른 모든 것을 인쇄합니다.
동일한 알고리즘 perl
:
perl -lne 's/[[:space:];#\/].*//;
next if (m/\.0$|[0-9a-f]{1,4}:|^\s*$/o);
print'
각 호스트에서 다운로드한 전체 파일을 사용하여 각 메소드를 10회 연속으로 실행하는 시간 제한 테스트 스크립트의 출력:
$ ./timing.sh
input file sizes:
24K drop.txt
72K base_90days.txt
120K sinokoreacidr.txt
216K total
input file line count:
793 drop.txt
4997 base_90days.txt
5400 sinokoreacidr.txt
11190 total
tdickey.awk: real 0m0.367s user 0m0.305s sys 0m0.027s
terdon.grep: real 0m0.550s user 0m0.514s sys 0m0.029s
cas.sed : real 0m0.531s user 0m0.484s sys 0m0.035s
cas.perl : real 0m0.379s user 0m0.341s sys 0m0.036s
output line counts:
4990 out.cas.perl
4990 out.cas.sed
4990 out.tdickey.awk
4990 out.terdon.grep
output differences (if any):
(그런데 timing.sh 테스트 스크립트는 원래 sed 스크립트에서 버그를 발견했습니다. 일부 줄은 후행 /CIDR로 인쇄되었습니다. 수정됨)
이들 모두는 정확히 동일한 출력을 생성하므로 좋습니다. :)
AMD Phenom II 1090T에서 여러 번 실행했습니다. sed
버전은 grep
상대적으로 안정적인 타이밍을 가지며 실행 간 차이가 거의 없으며 최대 1~2밀리초입니다.
awk
버전은 perl
실행마다 약간 더 다양합니다. 최대 20ms 정도입니다. 거의 항상 서로 몇 밀리초 이내에 있습니다. 때로는 perl
조금 더 빠르며, 일반적으로 awk
조금 더 빠릅니다. 아마도 내 시스템에 동시에 실행되는 다른 작업이 많이 있기 때문일 것입니다.
이 CPU에서는 두 버전 중 하나를 실행하는 데 걸리는 시간이 짧기 때문에 두 버전 사이에 큰 차이가 없습니다. 느린 CPU에서는 차이가 더 커질 수 있습니다. 여러분의 시스템에서 테스트할 수 있도록 아래에 타이밍 스크립트를 포함시켰습니다.
#!/bin/bash
export TIMEFORMAT=$'real %3lR\tuser %3lU\tsys %3lS'
files=(drop.txt base_90days.txt sinokoreacidr.txt)
function timetest() {
# first arg is title string, remaining args are executed.
# prime the cache
cat "${files[@]}" > /dev/null
title="$1" ; shift
printf '%-11s' "$title" >&2
# 10 runs for each
time for i in {1..10} ; do
"$@" "${files[@]}" > "out.$title"
done
# unique sort the output, but don't include sort in timings
sort -u "out.$title" > "out.tmp" ; mv -f out.tmp "out.$title"
}
echo 'input file sizes:'
du -sch "${files[@]}"
echo
echo 'input file line count:'
wc -l "${files[@]}"
echo
rm -f out.*
timetest tdickey.awk ./tdickey.awk
timetest terdon.grep grep -h -o '[0-9]\{1,3\}\.[0-9]\{1,3\}\.[0-9]\{1,3\}\.[1-9][0-9]*'
timetest cas.sed sed -E -e 's/[[:space:];#\/].*//; /\.0$|[0-9a-f]{1,4}:|^[[:space:]]*$/d'
timetest cas.perl perl -lne 's/[[:space:];#\/].*//; next if (m/\.0$|[0-9a-f]{1,4}:|^\s*$/o); print'
echo
echo "output line counts:"
wc -l out.* | grep -v total
# check if they all produce exactly the same output
echo
echo "output differences (if any):"
diff -u out.cas.sed out.cas.perl
diff -u out.cas.sed out.tdickey.awk
diff -u out.cas.sed out.terdon.grep
답변4
음, 정규식에 적절한 패턴을 추가하기만 하면 됩니다. 대신 [0-9]\{1,3\}
표현식 끝에 이와 같은 내용을 추가해야 합니다 .[0-9]*[1-9]