인접한 행에서 일치하는 항목 찾기

인접한 행에서 일치하는 항목 찾기

예를 들어 패턴 일치가 다음과 같은 경우 인접한 일치 선을 찾고 싶습니다.

$ grep -n pattern file1 file2 file3
file1:10: ...
file2:100: ...
file2:1000: ...
file2:1001: ...
file3:1: ...
file3:123: ...

중간에 있는 두 개의 일치 항목을 찾고 싶습니다.

file2:1000: ...
file2:1001: ...

그러나 처음 두 개와 마지막 두 개는 아닙니다.

답변1

thrig와 동일한 테스트 파일을 사용하겠습니다.

$ cat file
a
pat 1
pat 2
b
pat 3

awk의 솔루션은 다음과 같습니다.

$ awk '/pat/ && last {print last; print} {last=""} /pat/{last=$0}' file
pat 1
pat 2

어떻게 작동하나요?

awk파일의 각 줄을 암시적으로 반복합니다. 프로그램은 last정규식과 일치하는 경우 마지막 줄을 포함하는 변수를 사용합니다 pat. 그렇지 않으면 빈 문자열이 포함됩니다.

  • /pat/ && last {print last; print}

    pat이 줄이 일치하고 이전 줄 도 일치 하면 last두 줄을 모두 인쇄합니다.

  • {last=""}

    last빈 문자열로 바꾸기

  • /pat/ {last=$0}

    행이 일치하면 해당 행으로 pat설정됩니다 . last이렇게 하면 다음 행을 처리할 때 사용할 수 있습니다.

2개의 연속 게임을 하나의 그룹으로 처리하는 대체 방법

다음 확장 테스트 파일을 고려해 보겠습니다.

$ cat file2
a
pat 1
pat 2
b
pat 3
c
pat 4
pat 5
pat 6
d

위의 솔루션과 달리 이 코드는 세 개의 연속된 일치하는 줄을 인쇄할 세트로 처리합니다.

$ awk '/pat/{f++; if (f==2) print last; if (f>=2) print; last=$0; next} {f=0}' file2
pat 1
pat 2
pat 4
pat 5
pat 6

이 코드는 두 개의 변수를 사용합니다. 이전과 동일합니다. last이전 줄입니다. 또한 f연속 일치 횟수도 계산됩니다. 따라서 f2 이상이면 일치하는 줄을 인쇄합니다.

grep과 유사한 기능 추가

grep질문에 표시된 출력을 시뮬레이션하기 위해 이 버전은 일치하는 각 줄 앞에 파일 이름과 줄 번호를 인쇄합니다.

$ awk 'FNR==1{f=0} /pat/{f++; if (f==2) printf "%s:%s:%s\n",FILENAME,FNR-1,last; if (f>=2) printf "%s:%s:%s\n",FILENAME,FNR,$0; last=$0; next} {f=0}' file file2
file:2:pat 1
file:3:pat 2
file2:2:pat 1
file2:3:pat 2
file2:7:pat 4
file2:8:pat 5
file2:9:pat 6

awk의 FILENAME 변수는 파일 이름을 제공하고 awk의 FILENAME 변수는 FNR파일 내의 줄 번호를 제공합니다.

각 파일의 시작 부분에서 0으로 FNR==1재설정됩니다 . f이렇게 하면 파일의 마지막 줄이 고려되지 않습니다.계속해서다음 파일의 첫 번째 줄로.

코드를 여러 줄에 걸쳐 분산시키려는 경우 위의 코드는 다음과 같습니다.

awk '
    FNR==1{f=0}
    /pat/ {f++
        if (f==2) printf "%s:%s:%s\n",FILENAME,FNR-1,last
        if (f>=2) printf "%s:%s:%s\n",FILENAME,FNR,$0
        last=$0
        next
    }

    {f=0}
    ' file file2

답변2

한 가지 방법은 이전 줄을 저장하고 현재 줄과 이전 줄이 일치할 때 인쇄하는 것입니다.

bash-4.1$ (echo a; echo pat 1; echo pat 2; echo b; echo pat 3)
a
pat 1
pat 2
b
pat 3
bash-4.1$ (echo a; echo pat 1; echo pat 2; echo b; echo pat 3) | \
          perl -nle 'print "$prev\n$_" if /pat/ and $prev =~ /pat/; $prev=$_'
pat 1
pat 2

그러나 인접한 행 일치 항목이 3개 이상 있는 경우 행이 두 번 이상 쌍으로 일치하므로 중복 일치가 발생합니다. 더 나은 옵션은 이전에 일치한 줄 수를 추적하고 일부 테스트 코드를 작성하여 다양하고 복잡한 경우(예: 파일 끝에 있는 블록)가 올바르게 처리되는지 확인하는 것입니다.

#!/usr/bin/env perl
use strict;
use warnings;

my $prev;
my $pattern = qr/pat/;
my $have_matches = 0;

while (my $line = readline) {
  if ($line =~ /$pattern/) {
    print $prev if $have_matches == 1;
    print $line if $have_matches;
    $have_matches++;
    $prev = $line;
  } else {
    $have_matches = 0;
  }
}

답변3

기록을 위해 다음을 통해 이 작업을 수행할 수도 있습니다 sed.

sed -s '$!N
/.*PATTERN.*\n/{/\n.*PATTERN/{x;/^1$/!s/.*/1/;b v};//!{x;/^1$/{s/./0/;b v};//!D}}
//!{${/PATTERN/{x;/^1$/{b v}}};D;};: v;x;P;D' file1 file2 ... fileN

그건 gnu sed. 다른 sed파일의 경우 한 번에 하나의 파일을 처리해야 합니다.

sed '$!N                   # if not on the last line pull in the next line
/.*PATTERN.*\n/{           # if first line in the pattern space matches
/\n.*PATTERN/{             # and if second line also matches                   
x                          # exchange pattern space with hold buffer
/^1$/!s/.*/1/              # replace everything with 1
b v                        # branch to label v
}
//!{                       # if second line does not match
x                          # exchange pattern space with hold buffer
/^1$/{                     # if it matches 1
s/.*/0/                    # replace with 0
b v                        # branch to label v
}
//!D                       # if it does not match 1 delete up to first newline
}
}
//!{                       # if first line does not match
${                         # if we're on the last line
/PATTERN/{                 # and if it matches
x                          # exchange pattern space with hold buffer
/^1$/{                     # if it matches 1
b v                        # branch to label v
}
}
}
D                          # else delete up to first newline
}
: v                        # label v
x                          # exchange pattern space with hold buffer
P                          # print up to first newline
D' infile                  # delete up to first newline

perl또는 awk만큼 유연 하지 않습니다 . 출력을 완전히 시뮬레이션할 수는 없습니다. 즉, 줄 앞에 파일 이름과 줄 번호를 붙입니다. 하지만 grep앞에 추가한 다음 전체 출력을 파이핑하여 gnu sed파일 이름을 얻을 수는 있습니다.FPpaste -d: - -

답변4

안녕하세요, 마지막 줄을 완성하는 데 도움이 되는 다양한 명령이 있습니다. 이것을 시도해 보세요..

<grep command> | tail -1

또는

awk '/result/ { save=$0 }END{ print save }' filename

관련 정보