隣接する一致する線を見つけたい、たとえば、パターンが一致する場合
$ grep -n pattern file1 file2 file3
file1:10: ...
file2:100: ...
file2:1000: ...
file2:1001: ...
file3:1: ...
file3:123: ...
真ん中の2つの一致を見つけたい:
file2:1000: ...
file2:1001: ...
しかし、最初の2つと最後の2つではありません。
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
は、ファイル内のすべての行を暗黙的にループします。このプログラムは、1つの変数last
を使用します。これには、正規表現pat
と一致した場合の最後の行が含まれます。それ以外の場合は、空の文字列が含まれます。
/pat/ && last {print last; print}
pat
がこの行と一致し、前の行last
も一致した場合は、両方の行を出力します。
{last=""}
last
を空の文字列に置き換えます
/pat/ {last=$0}
この行がpat
と一致する場合は、last
をこの行に設定します。このようにして、次の行を処理するときに使用できるようになります。
この拡張テストファイルについて考えてみましょう。
$ cat file2
a
pat 1
pat 2
b
pat 3
c
pat 4
pat 5
pat 6
d
上記のソリューションとは異なり、このコードは3つの連続する一致する行を1つのグループとして印刷対象として扱います。
$ 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
このコードは2つの変数を使用します。前と同じように、last
は前の行です。さらに、f
は連続した一致の数をカウントします。したがって、f
が2以上の場合、一致する行を出力します。
質問に示されている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のFNR
はファイル内の行番号を提供します。
各ファイルの先頭で、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
1つの方法は、前の行を保存し、現在の行と前の行の両方が一致したときに印刷することです。
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つ以上ある場合、これらはペアごとに2回以上一致するため、重複一致が発生します。より良いオプションは、一致する前の行の数を追跡し、さまざまな厄介なEdgeケース(ファイルの終わりまでのブロックなど)が適切に処理されることを確認するためのテストコードを作成することです。
#!/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;
}
}