그렙 매치 및 추출


10

나는 줄을 포함하는 파일을 가지고있다.

proto=tcp/http  sent=144        rcvd=52 spkt=3 
proto=tcp/https  sent=145        rcvd=52 spkt=3
proto=udp/dns  sent=144        rcvd=52 spkt=3

나는이 프로토의 값을 추출해야 tcp/http, tcp/https, udp/dns.

지금까지 나는 이것을 시도 grep -o 'proto=[^/]*/'했지만 값을로만 추출 할 수 proto=tcp/있습니다.



이 작업은 sed, awk또는 perl아닙니다 grep.
OrangeDog 2016 년

답변:


1

이것이 이전 질문 과 관련이 있다고 가정하면 잘못된 길을 가고 있습니다. 대부분의 시간 동안 원하는 것을 수행하고 아주 조금 다른 것을해야 할 때마다 완전히 다른 스크립트를 가져와야하는 약간의 스크립트를 모 으려고 시도하는 대신 구문 분석 할 수있는 하나의 스크립트를 작성하십시오. 입력 파일을 배열 ( f[]아래)에 입력 하여 필드 이름 (태그)을 해당 값에 매핑 한 다음 결과에서 원하는 모든 작업을 수행 할 수 있습니다 (예 : 이전 질문의 입력 파일).

$ cat file
Feb             3       0:18:51 17.1.1.1                      id=firewall     sn=qasasdasd "time=""2018-02-03"     22:47:55        "UTC""" fw=111.111.111.111       pri=6    c=2644        m=88    "msg=""Connection"      "Opened"""      app=2   n=2437       src=12.1.1.11:49894:X0       dst=4.2.2.2:53:X1       dstMac=42:16:1b:af:8e:e1        proto=udp/dns   sent=83 "rule=""5"      "(LAN->WAN)"""

이름 / 태그로 색인 된 값의 배열을 만드는 awk 스크립트를 작성할 수 있습니다.

$ cat tst.awk
{
    f["hdDate"] = $1 " " $2
    f["hdTime"] = $3
    f["hdIp"]   = $4
    sub(/^([^[:space:]]+[[:space:]]+){4}/,"")

    while ( match($0,/[^[:space:]]+="?/) ) {
        if ( tag != "" ) {
            val = substr($0,1,RSTART-1)
            gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
            f[tag] = val
        }

        tag = substr($0,RSTART,RLENGTH-1)
        gsub(/^"|="?$/,"",tag)

        $0 = substr($0,RSTART+RLENGTH)
    }

    val = $0
    gsub(/^[[:space:]]+|("")?[[:space:]]*$/,"",val)
    f[tag] = val
}

그리고 -e파일에서 스크립트를 명령 줄 스크립트와 쉽게 믹싱 하기 위해 GNU awk를 사용하는 등 필드 이름으로 데이터를 참조하여 원하는대로 무엇이든 할 수 있습니다 .

$ awk -f tst.awk -e '{for (tag in f) printf "f[%s]=%s\n", tag, f[tag]}' file
f[fw]=111.111.111.111
f[dst]=4.2.2.2:53:X1
f[sn]=qasasdasd
f[hdTime]=0:18:51
f[sent]=83
f[m]=88
f[hdDate]=Feb 3
f[n]=2437
f[app]=2
f[hdIp]=17.1.1.1
f[src]=12.1.1.11:49894:X0
f[c]=2644
f[dstMac]=42:16:1b:af:8e:e1
f[msg]="Connection"      "Opened"
f[rule]="5"      "(LAN->WAN)"
f[proto]=udp/dns
f[id]=firewall
f[time]="2018-02-03"     22:47:55        "UTC"
f[pri]=6

$ awk -f tst.awk -e '{print f["proto"]}' file
udp/dns

$ awk -f tst.awk -e 'f["proto"] ~ /udp/ {print f["sent"], f["src"]}' file
83 12.1.1.11:49894:X0

2
이 : 김 감사합니다, 굉장
user356831

이런 종류의 작업에는 perl사용하기가 더 쉬울 수 있습니다.
OrangeDog 2016 년

1
@OrangeDog 왜 그렇게 생각하십니까? 나는 당신이 그런 대답을 게시하는 것을 신경 쓰지 않는다면 실제로 펄에서 동등한 것을보고 싶습니다. 펄은 내가 상자에없고 그것을 설치할 수 없다면 확실히 사용하기 쉽지 않을 것이다. 그러나 그것은 내가 수년에 걸쳐 자주 다루어야하는 것이었다. 반면에 Awk는 필수 유틸리티이므로 sed, grep, sort 등과 같은 UNIX 설치에는 항상 존재합니다.
Ed Morton

@EdMorton true, perl이 기본적으로 포함되지 않은 배포판을 개인적으로 본 적이 없습니다. 복잡한 작업 awksed스크립트는 일반적으로 perl일반적인 작업에 대한 추가 기능이있는 기본적으로 상위 집합이기 때문에 더 간단 합니다.
OrangeDog 2016 년

@OrangeDog 누구도 sed 스크립트보다 더 복잡 s/old/new/g하고 sed가 이상 하지 않으므로 sed 스크립트를 작성해서는 안됩니다 . 복잡한 awk 스크립트가 perl에서 더 단순하다는 것에 전혀 동의하지 않습니다. 물론 더 짧을 수 있지만 간결함은 소프트웨어의 바람직한 속성이 아니며 간결함은 없으며 실제 이익을 얻는 것은 극히 드물며 일반적으로 사람들이 zoitz.com 과 같은 것을 게시하는 것은 훨씬 어렵습니다. / archives / 13 perl에 대해 awk와 달리 쓰기 전용 언어로 참조하십시오. 그래도 여전히 이것과 동등한 펄을보고 싶습니다
Ed Morton

13

를 사용하면 grep -o추출하려는 것과 정확히 일치해야합니다. proto=문자열 을 추출하지 않으려면 일치하지 않아야합니다.

슬래시와 비어 있지 않은 영숫자 문자열 중 하나 tcp이상 과 일치하는 확장 정규식은 udp다음과 같습니다.

(tcp|udp)/[[:alnum:]]+

이것을 데이터에 적용 :

$ grep -E -o '(tcp|udp)/[[:alnum:]]+' file
tcp/http
tcp/https
udp/dns

문자열로 시작하는 행에서만이 작업을 수행하십시오 proto=.

grep '^proto=' file | grep -E -o '(tcp|udp)/[[:alnum:]]+'

을 사용 하여 첫 번째 공백 문자 sed앞과 첫 =번째 공백 문자 뒤의 모든 것을 제거하십시오 .

$ sed 's/^[^=]*=//; s/[[:blank:]].*//' file
tcp/http
tcp/https
udp/dns

string으로 시작하는 행에서만이 작업을 수행하려면 위와 proto=동일한 사전 처리 단계를 삽입 grep하거나

sed -n '/^proto=/{ s/^[^=]*=//; s/[[:blank:]].*//; p; }' file

여기서는 -n옵션 으로 기본 출력을 억제 한 다음 행이 일치하는 경우에만 행의 대체 및 명시 적 인쇄를 트리거합니다 ^proto=.


awk사용하여 기본 필드 구분 기호를 사용한 다음 첫 번째 필드를 분할 =하고 두 번째 비트를 인쇄하십시오.

$ awk '{ split($1, a, "="); print a[2] }' file
tcp/http
tcp/https
udp/dns

string으로 시작하는 행에서만이 작업을 수행하려면 위와 proto=동일한 사전 처리 단계를 삽입 grep하거나

awk '/^proto=/ { split($1, a, "="); print a[2] }' file

10

GNU grep을 사용하는 경우 ( -P옵션) 다음을 사용할 수 있습니다.

$ grep -oP 'proto=\K[^ ]*' file
tcp/http
tcp/https
udp/dns

여기에서 proto=문자열 을 일치시켜 올바른 열을 추출하고 \K플래그 와 함께 출력에서 ​​버립니다 .

위의 열은 공백으로 구분되어 있다고 가정합니다. 탭도 유효한 구분 기호 인 경우 \S공백이 아닌 문자를 일치시키는 데 사용 되므로 명령은 다음과 같습니다.

grep -oP 'proto=\K\S*' file

proto=a와 같은 하위 문자열 인 일치 필드를 방지 thisisnotaproto=tcp/https하려면 다음과 \b같이 단어 경계를 추가 할 수 있습니다 .

grep -oP '\bproto=\K\S*' file

1
당신은 그냥 작성하여 향상시킬 수 있습니다 grep -oP 'proto=\K\S+'. proto=tcp/http뒤에 공백 대신 탭 이 올 수 있으며 공백이 아닌 문자 와 \S는 다릅니다 [^ ].
mosvy 2016 년

@ mosvy : 좋은 제안입니다, 감사합니다.
user000001 2016 년

1
어쨌든 -oGNUism이기도합니다. PCRE 지원으로 빌드 된 경우 -PGNU에서만 지원됩니다 grep(빌드시 선택 사항).
Stéphane Chazelas 2018 년

6

사용 awk:

awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input

$1 ~ "proto"proto첫 번째 열의 행에서만 조치를 취할 것입니다.

sub(/proto=/, "")proto=입력에서 제거 합니다

print $1 나머지 열을 인쇄합니다


$ awk '$1 ~ "proto" { sub(/proto=/, ""); print $1 }' input
tcp/http
tcp/https
udp/dns

3

grep솔루션의 코드 골프

grep -Po "..p/[^ ]+" file

또는

grep -Po "..p/\S+" file


2

또 다른 grep해결책 :

grep -o '[^=/]\+/[^ ]\+' file

sed일치하는 캡처 그룹 만 인쇄 하는 것과 비슷한 것입니다 .

sed -n 's/.*=\([^/]\+\/[^ ]\+\).*/\1/p' file

1

다른 awk접근법 :

$ awk -F'[= ]' '/=(tc|ud)p/{print $2}' file
tcp/http
tcp/https
udp/dns

awk의 필드 구분 기호 =또는 공백으로 설정됩니다. 라인이 일치하면, =다음 중 하나 ud또는 tca로 그 다음을 p, 제 2 필드를 인쇄 할 수 있습니다.

또 다른 sed접근법 (의 모든 버전에 이식 가능 sed하지는 않지만 GNU와 함께 작동 sed) :

$ sed -En 's/^proto=(\S+).*/\1/p' file 
tcp/http
tcp/https
udp/dns

-n수단 "인쇄되지 않습니다"하고는 -E우리에게 확장 정규 표현식 수 있습니다 \S, "공백이 아닌"에 대한을 +"하나 이상"또는 캡처 괄호를 위해. 마지막으로 /p작업이 성공한 경우에만 대체 연산자와 일치하는 경우에만 sed가 행을 인쇄합니다.

그리고 펄 하나 :

$ perl -nle '/^proto=(\S+)/ && print $1' file 
tcp/http
tcp/https
udp/dns

-n수단 "라인으로 입력 파일 라인을 읽고에 의해 주어진 스크립트 적용 -e각 라인을". 는 -l각각 개행 추가 print전화 (그리고 입력으로부터 출사 바꿈 제거). 스크립트 자체는 a 다음에 나오는 공백이 아닌 가장 긴 문자를 인쇄합니다 proto=.


1
-E점점 더 이식성이 높아지고 있지만 \S그렇지 않습니다. [^[:space:]]보다 휴대하기 쉬운 제품입니다.
Stéphane Chazelas

1

여기 또 다른 쉬운 해결책이 있습니다.

grep -o "[tc,ud]*p\\/.*  "   INPUTFile.txt  |   awk '{print $1}'

당신은 grep아무것도 일치하지 않습니다. , 또는 , 또는 또는 또는 [tc,ud]\*\\/.* 번 찾은 다음 리터럴 문자, a 및 백 슬래시를 찾습니다 . 당신은 아마 의미했다 . 그러나 awk를 사용하는 경우 awk에서 모든 작업을 수행 할 수도 있습니다 . tc,ud*pgrep -Eo '(tc|ud)p/.* ' file | awk '{print $1}'awk -F'[= ]' '/(tc|ud)p/{print $2}' file
terdon

누군가 내 원본을 수정했습니다. 별 앞에 여분의 백 슬래시가 있었는데 방금 선생님을 제거했습니다.
mkzia 2016 년

편집 해 주셔서 감사하지만 우연히 작동하는 것이 두렵습니다. 내가 전에 설명한 것처럼, [tc,ud]p수단 "중 하나는 t, c, ,, u또는 da로 다음 p에만 있기 때문에 여기에 일치하도록. tcpcpudp있다 dp. 그러나 그것은 또한 일치하는 것 ,p또는 tp등 또한, 지금 당신은을 가지고 *, 그것은 일치 ppp(뿐만 아니라 *수단 "0 이상"이 일치하지 않는 경우에도이) 일치 있도록 (문자 클래스를 원하지 않는다. [ ]), 당신이 원하는 것은 그룹이다 : (tc|ud)와 (사용 -E의 플래그 grep.) 또한,이 .*그것을 만들어 전체 라인과 일치
terdon

1
@Jesse_b : mkzia는 기술적 으로“새로운 기고자”가 아니지만, 명령에 코드 형식을 사용하지 않았다는 사실에 의해 경험이 부족한 사용자입니다. 그러나 명령 \*에서 첫 번째 *를 기울임 꼴로 표시하지 않고 *로 표시 할 수 있을 정도로 똑똑 했습니다. 명령을 코드 형식으로 넣으면 \이전이 *나타나게되므로 명령이 실패하게됩니다. 다른 사람의 게시물을 편집 할 때 이와 같이 게시물의 모양을 변경해야합니다.
G-Man, 'Reinstate

@terdon : (1) 아니요, 실제로는 일치하지 않습니다 ppp. 물론 당신은 일치를 잘한다는 것 ,p나  tp- 나 uucp, ttp, cutp, ductp또는 d,up.
G-Man, 'Reinstate


0
cat file| cut -f1 -d' '| cut -f2 -d'='
tcp/http
tcp/https
udp/dns

컷 옵션 :

  • -f - 들
  • -d -델리 미터
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.