순서에 관계없이 행에서 여러 패턴을 추출합니다.

순서에 관계없이 행에서 여러 패턴을 추출합니다.

저는 Unix 스크립팅이 처음이므로 양해해 주시기 바랍니다.

한 줄당 프로세스에 대한 정보가 포함된 파일을 얻습니다. 각 행에서 이러한 프로세스에 대한 특정 정보를 추출해야 합니다.

파일 예시 -

process1 port=1234 appID=dummyAppId1 authenticate=true <some more params>
process3 port=1244 authenticate=false appID=dummyAppId2 <some more params>
process2 appID=dummyAppId3 port=1235 authenticate=true <some more params>

원하는 출력은 -

1
port=1234 authenticate=true appID=dummyAppId1 
2
port=1244 authenticate=false appID=dummyAppId2
3
port=1235 authenticate=true appID=dummyAppId3

각 줄의 숫자 1, 2, 3은 출력 파일의 줄 번호만 나타냅니다.

명령 을 사용해 보았지만 sed s/순서에 따라 다르며 입력 파일의 매개변수가 순서를 따르지 않습니다. 따라서 입력 파일의 일부 줄을 건너뛰었습니다.

이것이 내 명령이다 -

sed -nr 'appId/s/(\w+).*port=([^ ]+) .*authenticate=[^ ]+) .*appId=[^ ]+) .*/\2\t\3\t\4/p' | sed =

순서에 상관없이 이러한 매개변수를 추출하는 방법을 안내해 줄 수 있는 사람이 있나요?

감사해요!

편집 1: 저는 grep의 너비가 0인 뒷모습 어설션 기능을 이런 식으로 사용할 수 있었습니다.

grep -Po '(?<=pattern1=)[^ ,]+|(?<=pattern2=)[^ ,]+|(?<=pattern3=)[^ ,]+|(?<=pattern4=)[^ ,]+' filename

그러나 이것은 새 줄의 각 줄에 대한 출력을 제공하는 것 같습니다.

1234
true
dummyAppId1

grep을 사용하여 한 줄에 배치하는 방법을 알아내려고 합니다(즉, X 줄을 1로 병합하지 않음).

편집 2: 입력의 매개변수 순서를 혼동함

편집 3: 죄송합니다. 앞서 언급했어야 했는데, perl제가 작업하는 컴퓨터에서는 제한적인 것 같습니다. Stephane과 Sundeep이 제공한 답변은 로컬에서 테스트했을 때 완벽하게 작동했지만 궁극적으로 실행하는 데 필요한 컴퓨터에서는 작동하지 않았습니다. awk, grep 및 sed가 주요 지원 옵션인 것 같습니다.

답변1

사용 awk(테스트됨 GNU awk, 다른 구현에서도 작동하는지 확실하지 않음)

$ cat kv.awk
/appID/ {
    for (i = 1; i <= NF; i++) {
        $i ~ /^port=/ && (a = $i)
        $i ~ /^authenticate=/ && (b = $i)
        $i ~ /^appID=/ && (c = $i)
    }
    print NR "\n" a, b, c
}

$ awk -v OFS='\t' -f kv.awk ip.txt
1
port=1234   authenticate=true   appID=dummyAppId1
2
port=1244   authenticate=false  appID=dummyAppId2
3
port=1235   authenticate=true   appID=dummyAppId3


그리고perl

$ # note that the order is changed for second line here
$ cat ip.txt
process1 port=1234 authenticate=true appID=dummyAppId1 <some more params>
process3 port=1244 appID=dummyAppId2 authenticate=false <some more params>
process2 port=1235 authenticate=true appID=dummyAppId3 <some more params>

$ perl -lpe 's/(?=.*(port=[^ ]+))(?=.*(authenticate=[^ ]+))(?=.*(appID=[^ ]+)).*/$1\t$2\t$3/; print $.' ip.txt 
1
port=1234   authenticate=true   appID=dummyAppId1
2
port=1244   authenticate=false  appID=dummyAppId2
3
port=1235   authenticate=true   appID=dummyAppId3
  • (?=.*(port=[^ ]+))첫 번째 캡처 그룹port
  • (?=.*(authenticate=[^ ]+))두 번째 캡처 그룹 authenticate
  • print $.줄 번호에 대해
  • 부분 일치를 피하려면 단어 경계가 충분할 경우 etc를 사용하십시오 \bport. \bappID그렇지 않으면 (?<!\S)(port=[^ ]+)공간 기반 제한이 사용됩니다.

포함된 행만 인쇄해야 하거나 appID그러한 조건이 다른 경우 다음 -lpe으로 변경 -lne하고 다음 print $.으로 변경하십시오.print "$.\n$_" if /appID/

답변2

의 경우 perl다음과 같이 사용할 수 있습니다.

perl -lne 'my %h;
           $h{$1} = $& while /(\S+?)=(\S+)/g;
           print "@h{qw(port authenticate appID)}"'

키가 속성 이름이고 값이 s인 해시 테이블을 만들고 name=value원하는 것을 인쇄할 수 있습니다.

값만 출력하고 싶다면 $&로 바꾸세요.$2

awk같은

awk '
  {
    split("", h)
    for (i = 1; i <= NF; i++)
      if (n = index($i, "=")) h[substr($i, 1, n - 1)] = $i
    print h["port"], h["authenticate"], h["appID"]
  }'

를 사용하면 pcregrep다음을 수행할 수 있습니다.

pcregrep -o1 -o2 -o3 --om-separator=' ' '(?x)
  ^(?=.*?\s(port=\S+))
   (?=.*?\s(authenticate=\S+))
   (?=.*?\s(appID=\S+))'

(이렇게 하려면 세 가지 속성이 모두 있어야 합니다.)

그리고 sed:

sed 'G
     s/[[:space:]]\(port=[^[:space:]]*\).*\n.*/&\1/
     s/[[:space:]]\(authenticate=[^[:space:]]*\).*\n.*/& \1/
     s/[[:space:]]\(appID=[^[:space:]]*\).*\n.*/& \1/
     s/.*\n//'

마지막 두 개는 속성이 줄의 첫 번째 단어가 아니라고 가정합니다(이는 샘플에서 합리적인 가정처럼 보입니다).

답변3

편집 3에 따르면 아래와 같이 각 매개변수에 대한 표현식을 sed생성하면 여전히 이 작업을 수행할 수 있다고 생각합니다.s///

sed -nE 's/^(.*)(appID=[^[:blank:]]+\s)(.*)$/\2\t\1\3/
         s/^(.*)(authenticate=[^[:blank:]]+\s)(.*)$/\2\t\1\3/
         s/^(.*)(port=[^[:blank:]]+\s)(.*)$/\2\t\1\3/
         T;=
         s/^(([^[:blank:]]+\s+){,3}).*/\1/
         p'

s원하는 출력 순서를 기준으로 표현식의 역순을 확인하세요. 번호 매기기는 스크립트에도 포함되어 있습니다. 언급한 대로 줄 번호를 인쇄하고 필요한 인수 중 하나가 실제로 줄에 존재하는 경우에만 줄을 인쇄합니다. 또한 여러분 sed 은 \dBSD가 sed. POSIX 규격에 상응하는 것이 가능할 수도 있지만 더 확장될 수도 있습니다.

그러나 이는 이미 상당히 길고 출력 매개변수가 추가됨에 따라 더욱 복잡해지기 때문에 awk다음과 같은 스크립트가 더 일반적일 수 있습니다.

awk '
    BEGIN {ac=ARGC; ARGC=0; OFS="\t"}
    {
        str=$0; NF=0
        for (i=1; i<ac; i++)
            if (match(str, ARGV[i]"=[^[:blank:]]*"))
                $(NF+1)=substr(str, RSTART, RLENGTH)
    }
    NF {print ++nr; print}
    ' -- port authenticate appID

출력할 정확한 매개변수와 표시 순서를 지정할 수 있습니다 . 스크립트는 필수 매개변수 중 하나 이상이 실제로 행에 존재하는 경우에만 행을 인쇄합니다 awk .--

답변4

유사한 문제를 겪고 있는 다른 사용자에게 도움이 된다면 Ruby를 사용하여 (자세한) 제안을 해주세요:

# passing the log file as parameter
lines = File.open(ARGV[0]).read.split("\n")

lines.each_with_index do |line, i|
  words  = line.split(' ')
  output = []

  puts i + 1
  output << words.select { |w| w =~ /port=\d+/ }
  output << words.select { |w| w =~ /authenticate=\w+/ }
  output << words.select { |w| w =~ /appID=\w+/ }

  puts output.join(' ')
end

관련 정보