如何使用 grep 打印匹配行上方或下方的第 N 行和第 M 行?

如何使用 grep 打印匹配行上方或下方的第 N 行和第 M 行?

假设我有一个结构良好的文本文件,其中包含以下内容(没有前导行号):

 1 Mon Jun 9 00:11:47 CST 2014
 2 eth0      Link encap:Ethernet  HWaddr D4:BE:D9:F5:5C:0E
 3           inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
 4           inet6 addr: fe80::d6be:d9ff:fef5:5c0e/64 Scope:Link
 5           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
 6           RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
 7           TX packets:250825 errors:0 dropped:0 overruns:0 carrier:0
 8           collisions:0 txqueuelen:10000
 9           RX bytes:365792552 (348.8 MiB)  TX bytes:20648578 (19.6 MiB)
10           Interrupt:24 Memory:d6000000-d6012100
11 Tue Jun 10 05:11:47 CST 2014
12 eth1      Link encap:Ethernet  HWaddr D4:BE:D9:F5:5C:10
13           inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
14           inet6 addr: fe80::d6be:d9ff:fef5:5c10/64 Scope:Link
15           UP BROADCAST RUNNING MULTICAST  MTU:1500  Metric:1
16           RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0
17           TX packets:1206000723 errors:0 dropped:0 overruns:0 carrier:0
18           collisions:0 txqueuelen:10000
19           RX bytes:1596108082 (1.4 GiB)  TX bytes:2960952707 (2.7 GiB)
20           Interrupt:25 Memory:d8000000-d8012100

现在我需要的是使用 grep 关键字“eth”过滤 eth0 部分的第 1、#3、#6 行和 eth1 部分的第 #11、#13、#16 行。

Mon Jun 9 00:11:47 CST 2014    
inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
Tue Jun 10 05:11:47 CST 2014
inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0

我怎样才能做到这一点?

答案1

这对我有用:

sed -n '/eth/{n;p;n;n;n;p;}' file
  • 搜索字符串eth
  • n;: 跳过一行,p;: 打印该行
  • n;n;n;: 跳过3行并再次打印

编辑:

这将打印上面的行、后面的行以及 3 行之后的行:

sed -n -e '/eth/{x;1!p;g;$!n;p;n;n;n;p;D;}' -e h file

你的第二个问题:我认为你可能不会输入 20 次命令n;......

答案2

试试这个 awk 命令,

$ awk '$1~/^eth/ {getline; print; getline; getline; getline; print}' file
          inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
          RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
          inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
          RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0

更新:

$ awk '$1~/^eth/ {print previous; getline; print; getline; getline; getline; print}{previous=$0}' file
Mon Jun 9 00:11:47 CST 2014
           inet addr:10.179.113.125  Bcast:10.179.113.127  Mask:255.255.255.248
           RX packets:1169385 errors:0 dropped:0 overruns:0 frame:0
Tue Jun 10 05:11:47 CST 2014
           inet addr:10.254.4.1  Bcast:10.254.4.255  Mask:255.255.255.0
           RX packets:3806158038 errors:0 dropped:23193484 overruns:0 frame:0

答案3

这是一种使用 Perl 的不同方法,可以让您更灵活地选择要打印的行:

perl -n00E '
    @eth_records= grep {/eth/} split/(?=>Mon|Tue|Wed|Thu|Fri|Sat|Sun)/;
    @lines_to_print = qw{1 3 6};
    map { $_-- } @lines_to_print;
    $sep = "-"x80;
    for(@eth_records){
        say $sep;
        say for (split/\n/)[ @lines_to_print ];
        say $sep
    }' your_file

这会将文件分割成以一天名称开头的行记录,并打印所有匹配记录的第 1、3 和 6 行/eth/

请注意,它会将整个文件加载到内存中,因此如果您的文件很大,请避免使用它。

答案4

以下是一些建议:

  • grep

    grep -P 'CST|inet |RX p' file
    

    CST这将打印包含或inet后跟空格或的所有行RX pa。激活-PPerl 兼容正则表达式,让我们可以将其用作|逻辑 OR。您也可以使用以下任一方法实现相同的目标

    grep -E 'CST|inet |RX p' file
    

    或者

    grep  'CST\|inet \|RX p' file
    
  • sed

    sed -n '/CST\|inet \|RX p/p' file
    sed -rn '/CST|inet |RX p/p' file
    

    与上面的想法相同,-n抑制打印任何行,并//p打印那些与模式匹配的行。

  • perl

    您可以在 Perl 中使用相同的方法:

     perl -ne 'print if /CST|inet |RX p/' file
    

    或者你可以做类似的事情

     perl -ne '$k=1 if /CST/; print if $k==1||$k==3||$k==6; $k++' file
    

    此处,$k如果该行匹配,则该变量设置为 1 CST,并且在读取每行后该变量会增加 1。如果该行$k的值为 1,3 或 6,则打印该行。这是一种更具可扩展性的方法。

    另一种方法是,假设您知道要打印的行号,则直接直接打印它们($.是当前行号):

    perl -ne 'print if $.==1||$.==3||$.==6||$.==11||$.==13||$.==16' file
    

    或者,更惯用的说法是:

    perl -ne '@d=(1,3,6,11,13,16); print if $.~~@d' file
    

    最后,您还可以将整个文件加载到内存中并仅打印您关心的行:

    perl -e '@F=<>; print @F[0,2,5,10,12,15]' file
    
  • awk

    awk您可以在whereNR是当前行号中使用相同的基本方法:

    awk '/CST|inet |RX p/' file
    

    或者

    awk '{if(/CST/){k=1} if(k==1||k==3||k==6){print} k++;}' file
    

    或者

    awk 'NR==1||NR==3||NR==6||NR==11||NR==13||NR==16' file
    

相关内容