로그 파일에서 여러 개의 쉼표로 구분된 문자열을 추출하는 방법은 무엇입니까?

로그 파일에서 여러 개의 쉼표로 구분된 문자열을 추출하는 방법은 무엇입니까?

정규화된 필드/열에는 없지만 로그 파일에서 특정 문자열을 추출해야 합니다. 예를 들어:

date="2017-01-03 08:30:02 -0500",fac=f_kernel_ipfilter,area=a_general_area,type=t_attack,pri=p_major,hostname=hostname,category=policy_violation,event="ACL deny",attackip=1.1.1.1,attackzone=internal,app_risk=low,app_categories=remote-admin,netsessid=c550e586ba75a,src_geo=US,srcip=1.1.1.1,srcport=38256,srczone=internal,protocol=6,dst_geo=US,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All,reason="Traffic denied by policy.",application=SSH

srcip, srczone, 프로토콜, dstip, dstzone, dstport 및 rule_name을 얻고 싶습니다. 나는 현재 필요하지 않은 OUT 필드를 제거하기 위해 Perl 게으른 매칭을 사용하고 있습니다. ,dstport=80,로그 파일의 위치에 관계없이 이 8개의 문자열과 쉼표 내의 데이터만 가져오는 방법이 있습니까 ? 동일한 데이터에 대한 입력 위치가 다양하므로 이를 어렵게 만듭니다.

답변1

빠르고 더러운 방법은 다음과 같습니다 perl.

$ perl -F, -lane '@l = grep {/srcip|srczone|protocol|dstip|dstzone|dstport|rule_name/} @F; 
                  print join ",",@l' file 
srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All

이는 -aPerl을 awk처럼 동작하게 만들고 주어진 문자를 기반으로 입력 라인을 분할하여 -F배열의 요소로 저장합니다 @F. 그런 다음 grep배열을 만들고 배열의 대상 단어와 일치하는 요소를 유지하고 @l마지막으로 @l인쇄 연결에 쉼표를 사용합니다.

패턴 중 하나라도 하위 패턴이 될 수 있으면 이 작업은 실패합니다 ( foo=bar및 가 있다고 가정).foobar=baz

더 긴 대상 패턴 목록의 경우(실제 스크립트를 작성하지 않으려는 경우) 이를 배열에 저장하고 연결하여 |grep의 정규 표현식을 만들 수 있습니다. 각 패턴 주위에 추가 하여 \b하위 패턴이 일치하지 않도록 할 수도 있습니다 . 불필요한 임시 배열도 제거하면 다음과 같은 결과를 얻습니다.

$ perl -F, -lane '
    BEGIN{
     $pat="\\b" . join "\\b|",qw(srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=)
    } print join ",",grep {/$pat/}@F' file 
srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All

우리 상주 전문가는 sed awk 또는 [. ..] ~에서주문

죄송합니다. 하지만 이것은 정말 터무니없는 일입니다. 각 도구에서 이 작업을 수행하는 한 가지(여러 가지) 방법은 다음과 같습니다.

  1. Bourne 쉘(다시). 이것을 사용하지 마십시오. 나는 단지 그것이 가능하다는 것을 증명하기 위해 보여드리는 것뿐입니다.

    $ pat=(srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=); 
    $ o=""; while IFS=, read -a fields; do 
                for f in "${fields[@]}"; do 
                    for pat in "${pat[@]}"; do 
                        [[ $f =~ $pat ]] && o="$f,$o"
                    done 
                done
               done < file ; echo ${o%,}
    
  2. 대상 패턴을 파일에 저장합니다.

    $ cat patterns
    srcip
    srczone
    protocol
    dstip
    dstzone
    dstport
    rule_name
    

    그 다음에:

    $ awk -F, '(NR==FNR){ 
                    pat[$0]++; 
                    next;
                } 
                {
                    for(i=1;i<=NF;i++){ 
                        split($i,a,"="); 
                        if(a[1] in pat){
                            printf "%s=%s,",a[1],a[2]
                        }
                    }
                    print ""
                }' patterns file | sed 's/,$//'
    srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All
    
  3. sed(및 쉘)

    $ pat=(srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=);
    $ for p in ${pat[@]}; do 
        sed -E "s/.*($p[^,]*).*/\1/" file; done | 
            sed ':a;N;$!ba;s/\n/,/g'
    srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstzone=external,dstport=80,rule_name=Deny_All
    
  4. Bourne 쉘(또는 모든 POSIX 쉘) + sed(1. 하지 마세요. 가능하지만 바보입니다)

    $ set srcip= srczone= protocol= dstip= dstzone= dstport= rule_name=
    $ for f in "$@"; do sed "s/.*\($f[^,]*\).*/\1/" file; done | sed ':a;N;$!ba;s/\n/,/g'
    srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstzone=external,dstport=80,rule_name=Deny_All
    

답변2

GNU 솔루션 awk:

gawk -v OFS= -v FPAT=',(srcip|srczone|protocol|dstip|dstzone|dstport|rule_name)=[^,]*' -e 'NF > 0 { $1=$1; print }'

여기서는 GNU 특정 기능을 사용합니다 awk. 변수에 대해 정규식을 사용 하여 FPAT필드의 형식을 지정하여 정규식과 일치하는 줄의 모든 부분이 에 할당 되도록 합니다 $1...$n.$1$1$0$1...$n

답변3

이에 대해 조금 늦었지만 제안을 제공하겠습니다. 이러한 종류의 데이터는 map해싱에 적합합니다.

#!/usr/bin/env perl

use strict;
use warnings;
#for debugging - can be removed;
use Data::Dumper;

my @fields = qw ( srcip srczone protocol dstip dstzone dstport rule_name );

#read STDIN or files specified on command line (just like grep/sed/awk)
while ( <> ) {

   #split commas
   #then read key-value pairs. 
   my %row = map { m/(.*)=(.*)/ } split /,/;
   #for debugging:
   print Dumper \%row;

   #print fields tab-separated and in order as above. 
   print join "\t", @row{@fields};
}

oneliner-ify는 철자를 입력해야 하는 필드 목록이 있기 때문에 약간 더 어렵습니다. 하지만:

perl -lane -F, 'BEGIN { @k = qw ( srcip srczone protocol dstip dstzone dstport rule_name ) } %r = map { m/(.*)=(.*)/ } @F; print join "\t", @r{@k}'

답변4

세게 때리다

IFS=, read -r -a fields <<< "$date"
results=()
for keyval in "${fields[@]}"; do 
    IFS='=' read -r key value <<< "$keyval"
    case $key in 
        srcip|srczone|protocol|dstip|dstzone|dstport|rule_name) results+=("$keyval")
    esac
done
(IFS=,; echo "${results[*]}")
srcip=1.1.1.1,srczone=internal,protocol=6,dstip=2.2.2.2,dstport=80,dstzone=external,rule_name=Deny_All

관련 정보