查找相邻行的匹配项

查找相邻行的匹配项

我想找到相邻的匹配行,例如,如果模式匹配是

$ 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如果它与 regex 匹配,则该变量包含最后一行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提供文件内的行号。

在每个文件的开头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

然而,如果存在三个或更多相邻行匹配,这将导致重复匹配,因为这些行将成对匹配两次或多次。更好的选择是跟踪先前匹配的行数,并编写一些测试代码来确认各种复杂的边缘情况(例如文件末尾的块)得到正确处理。

#!/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

它不像perlawk那样灵活 - 你不能完全模拟grep输出,即带有文件名和行号的前缀行,尽管你可以通过在 之前添加然后将整个输出gnu sed通过管道传递到来获取文件名FPpaste -d: - -

答案4

您好,有各种命令可以帮助您完善最后一行,试试这个..

<grep command> | tail -1

或者

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

相关内容