예를 들어 패턴 일치가 다음과 같은 경우 인접한 일치 선을 찾고 싶습니다.
$ 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
연속 일치 횟수도 계산됩니다. 따라서 f
2 이상이면 일치하는 줄을 인쇄합니다.
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
파일 이름을 얻을 수는 있습니다.F
P
paste -d: - -
답변4
안녕하세요, 마지막 줄을 완성하는 데 도움이 되는 다양한 명령이 있습니다. 이것을 시도해 보세요..
<grep command> | tail -1
또는
awk '/result/ { save=$0 }END{ print save }' filename