awk를 사용하여 grep -f를 다시 만드는 방법

awk를 사용하여 grep -f를 다시 만드는 방법

불필요한 정보가 많이 포함된 대용량 파일이 있습니다. 나는 편집과 다음 사이의 부분에만 관심이 있으며 이를 하나의 항목으로 취급합니다. 이렇게 필터링했는데..

'

awk  'BEGIN {FS = "\n"; RS = ""; OFS = "\n" ;} {if (/intf/ && /addr/) { print $0"\n"}}' > outputfile

출력 파일의 예는 다음과 같습니다.

edit 114
set uuid 6cb43
set action accept
set srcintf "Port-ch40.1657"
set dstintf "any"
set srcaddr "1.1.1.1"
set dstaddr "all"
set schedule "always"
set service "ALL_ICMP" "icmp-echo-reply" "icmp-source-quench" "icmp-time-exceeded" "icmp-unreachable"
set logtraffic all
next

edit 330
set uuid 6d3d
set action accept
set srcintf "Po40.28"
set dstintf "any"
set srcaddr "all"
set dstaddr "2.2.2.2"
set schedule "always"
set service "ALL_ICMP" "icmp-echo-reply" "icmp-source-quench" "icmp-time-exceeded" "icmp-unreachable"
set logtraffic all
next

grep에는 파일에서 값을 가져올 수 있는 옵션이 있습니다(grep -f filterfile textfile). 필터 파일에 값이 포함되어 있다고 가정해 보겠습니다.

1.1.1.1
3.3.3.3

실제로는 더 많으므로 수동으로 입력하면 작동하지 않을 수 있습니다.

awk  'BEGIN {FS = "\n"; RS = ""; OFS = "\n" ;} {if (/intf/ && /addr/ &&(/1.1.1.1/||/3.3.3.3/)) { print $0"\n"}}' > outputfile

파일의 값을 처리하도록 awk 명령을 수정할 수 있습니까?

awk  'BEGIN {FS = "\n"; RS = ""; OFS = "\n" ;} {if (/intf/ && /addr/ &&(values_from_filterfile)) { print $0"\n"}}' > outputfile

답변1

귀하의 경우:

awk 'BEGIN {FS = "\n"; RS = ""; OFS = "\n" ;} {if (/intf/ && /addr/ &&(/1.1.1.1/||/3.3.3.3/)) { print $0"\n"}}' > outputfile

이것이 바로 당신이 해야 할 일입니다. 한 줄에 일부 IP 주소가 포함된 "match.list" 파일의 내용을 다음과 비교할 수 있습니다.

gawk '
  ( NR==FNR ) { # NR==FNR only when parsing the first file...
      ipreg=$0; # get one ip from the first file
      gsub(".", "\.", ipreg); #ensure each "." becomes "\." for the regex
      ipreg= "\<" ipreg "\>" # add beginning of word / end of word delimiters
      # that way 1.2.3.4 will NOT match: 11.2.3.42
      ipsreg=ipsreg sep ipreg; sep="|" # add it to the list of ipsreg
      # and sep only added before the 2+ elements as it is an empty string for the 1st
      next # skip everything else, we are parsing the first file...
    }

  
    ( /intf/ && /addr/ && ( $0 ~ ipsreg ) ) # default action will be print $0 if it matches...
    # and as ORS at that point will have been set to "\n\n",
    # it will print the record + an empty line after it
 ' match.list FS="\n" RS="" OFS="\n" ORS="\n\n" - > outputfile
   # the things between match.list and - will be seen as definitions to be done at that time,
   # as they contain a "=", and not be interpreted as filenames
   #  - : is STDIN, and will be the 2nd "file" parsed, where NR>FNR (FNR=for the current file, NR=from the beginning)

답변2

FWIW 나는 이것을 위해 정규식을 사용하지 않을 것입니다. 아래에 레이블(예: )을 해당 값(예: 또는 ) 에 v[]매핑하는 배열()을 만든 다음 배열 인덱스의 해시 조회를 수행하여 어느 것을 찾을 수 있습니다. 현재 블록에 존재하는 값과 관심 있는 태그를 라벨링합니다. 예를 들어 POSIX awk를 사용하면 다음과 같습니다.srcaddr"1.1.1.1""all"

$ cat tst.awk
NR==FNR {
    ips["\"" $0 "\""]
    next
}
$1 == "edit" {
    lineNr = 1
}
lineNr {
    tagFld = (NF > 2 ? 2 : 1)
    tag = $tagFld
    match($0,"^([[:space:]]*[^[:space:]]+){" tagFld "}[[:space:]]*")
    heads[tag] = substr($0,1,RLENGTH)
    v[tag] = substr($0,RLENGTH+1)
    tags[lineNr++] = tag

    if ( $1 == "next" ) {
        if (    (("srcintf" in v) && (v["srcaddr"] in ips)) \
             || (("dstintf" in v) && (v["dstaddr"] in ips)) \
           ) {
            for ( i=1; i<lineNr; i++ ) {
                tag = tags[i]
                print heads[tag] v[tag]
            }
            print ""
        }
        delete v
        lineNr = 0
    }
}

$ awk -f tst.awk filterfile textfile
edit 114
set uuid 6cb43
set action accept
set srcintf "Port-ch40.1657"
set dstintf "any"
set srcaddr "1.1.1.1"
set dstaddr "all"
set schedule "always"
set service "ALL_ICMP" "icmp-echo-reply" "icmp-source-quench" "icmp-time-exceeded" "icmp-unreachable"
set logtraffic all
next

이 구조를 사용하면 쉽게 테스트할 수 있습니다.또는 변경원하는 태그에 대한 값을 지정하고 전체 블록에 대해 정규식 비교를 수행하는 대신 각 블록의 각 태그 내용에 대한 보다 정확한 테스트를 작성합니다. 예를 들어 is , is 및 include 블록을 찾거나 출력하려는 ​​경우 다음 설정을 변경할 uuid6cb43있습니다 .schedulealwaysservice"icmp-time-exceeded"

        if (    (("srcintf" in v) && (v["srcaddr"] in ips)) \
             || (("dstintf" in v) && (v["dstaddr"] in ips)) \

이와 관련하여:

        if (    (v["uuid"] == "6cb43") \
             && (v["schedule"] == "always") \
             && (v["service"] ~ /"icmp-time-exceeded"/) \

인쇄하기 전에 라벨을 다른 값으로 설정하려면 v[]인쇄 루프 전에 라벨을 채울 수 있습니다.

$ cat tst.awk
NR==FNR {
    ips["\"" $0 "\""]
    next
}
$1 == "edit" {
    lineNr = 1
}
lineNr {
    tagFld = (NF > 2 ? 2 : 1)
    tag = $tagFld
    match($0,"^([[:space:]]*[^[:space:]]+){" tagFld "}[[:space:]]*")
    heads[tag] = substr($0,1,RLENGTH)
    v[tag] = substr($0,RLENGTH+1)
    tags[lineNr++] = tag

    if ( $1 == "next" ) {
        if (    (("srcintf" in v) && (v["srcaddr"] in ips)) \
             || (("dstintf" in v) && (v["dstaddr"] in ips)) \
           ) {
            v["action"] = "reject"
            v["dstaddr"] = "\"127.0.0.1\""
            for ( i=1; i<lineNr; i++ ) {
                tag = tags[i]
                print heads[tag] v[tag]
            }
            print ""
        }
        delete v
        lineNr = 0
    }
}

$ awk -f tst.awk filterfile textfile
edit 114
set uuid 6cb43
set action reject
set srcintf "Port-ch40.1657"
set dstintf "any"
set srcaddr "1.1.1.1"
set dstaddr "127.0.0.1"
set schedule "always"
set service "ALL_ICMP" "icmp-echo-reply" "icmp-source-quench" "icmp-time-exceeded" "icmp-unreachable"
set logtraffic all
next

답변3

특정 질문에만 답변하세요awk는 파일의 값을 처리할 수 있습니다.getline, 예, 파일 이름을 사용하여 명령 입력을 리디렉션 할 수 있습니다 <. BEGIN 블록 끝에 다음을 추가합니다.

getline <"filterfile";
fromfilter = $0;
gsub("\n","|",fromfilter);

getline에서 전체 파일을 읽도록 FS 및 RS를 설정했으므로 $0간단히 줄 구분 기호를 정규식 연산자로 바꿀 수 있습니다 |. 결과 변수를 사용하십시오 match.

if (/intf/ && /addr/ && match($0,fromfilter)) { print $0"\n"}

관련 정보