使用grep仅显示比赛的一部分

Meg*_*tal 5 unix linux shell grep gnu

我对是否可以在以下情况下使用单个grep命令感兴趣。

我有一个dhcpd.conf文件,其中定义了DHCP主机。给定主机名,我需要在dhcpd.conf文件中找到其MAC地址。我需要使用它来禁用其PXE引导配置,但这不是这个问题的一部分。

该文件的语法是统一的,但我仍然想使其更加简单。这是定义主机的方式:

    host client1 { hardware ethernet 12:23:34:56:78:89; fixed-address 192.168.1.11; filename "pxelinux.0"; }
    host client2 { hardware ethernet 23:34:45:56:67:78; fixed-address 192.168.1.12; filename "pxelinux.0"; }
    host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; filename "pxelinux.0"; }
    host client4 { hardware ethernet C1:CA:88:FA:F4:90; fixed-address 192.168.1.14; filename "pxelinux.0"; }
Run Code Online (Sandbox Code Playgroud)

我们假定所有配置仅占用一行,即使dhcpd.conf语法允许将选项分成几行也是如此。我们假设选项的顺序可能不同。

我想出了以下grep命令:

grep -o "^[^#]*host.*${DHCP_HOSTNAME}.*hardware ethernet.*..:..:..:..:..:..;" /etc/dhcp/dhcpd-hosts.conf
Run Code Online (Sandbox Code Playgroud)

应该忽略那些被注释的行,在令牌之间允许任意空格,并匹配直到MAC地址的末尾。当我运行它时,我得到如下代码:

host client1 { hardware ethernet 12:23:34:56:78:89;
Run Code Online (Sandbox Code Playgroud)

这很棒!但是关键是我只需要一个MAC地址,而没有前面的垃圾桶。现在我知道,使用另一个grep或cut或awk仅从此输出中剪切MAC地址将是微不足道的。但我想知道,是否有一种方法可以使用单个grep命令来获得最终结果,而不必将此输出通过管道传递到另一个过滤器中?显然,我不能忘记模式的开头,因为我想获得一个特定的主机名,因此匹配“ ..:..:..:..:..:..”将为我提供所有MAC地址。

再一次,我想要一个命令(不一定是grep),该命令仅从文件中切出正确的MAC地址。因此,我对那些说“ grep ... | grep ...”或“ grep ... | cut ...”等的解决方案不感兴趣。

当然,在实践中,如果我使用多个过滤器并将其通过管道传输,也不会发生任何不好的情况,我很好奇是否可以使用一个过滤器来解决。

我会将输出分配给变量。

bgo*_*dst 2

您可以使用 Perl 单行代码将文件的每一行与具有适当捕获组的单个正则表达式进行匹配,并且对于匹配的每一行,您可以打印子匹配。

有多种方法可以使用 Perl 来完成此任务。我建议采用这种perl -ne {program}习惯用法,它隐式循环 stdin 的行,并为每一行执行一次单行代码{program},并将当前行作为特殊变量提供$_。(注意:该-n选项不会导致$_在隐式循环的每次迭代结束时自动打印 的最终值,而这正是该-p选项的作用;即 。perl -pe {program}

下面是解决方案。请注意,我决定使用模糊-s选项传递目标主机名,该选项可以在参数后解析变量赋值规范{program},类似于 awk 的-v选项。(不可能使用该-n选项传递普通的命令行参数,因为隐式while (<>) { ... }循环会吞噬文件名的所有此类参数,但该-s机制提供了一个很好的解决方案。请参阅是否可以在以下情况下将命令行参数传递给@ARGV:使用 -n 或 -p 选项?。)这种设计不需要将$DHCP_HOSTNAME变量嵌入到{program}字符串本身中,这允许我们用单引号引用它并保存一些(实际上是 8 个)反斜杠。

DHCP_HOSTNAME='client3';
perl -nse 'print($1) if m(^\s*host\s*$host\s*\{.*\bhardware\s*ethernet\s*(..:..:..:..:..:..));' -- -host="$DHCP_HOSTNAME" <dhcpd.cfg;
## AB:CD:EF:01:23:45
Run Code Online (Sandbox Code Playgroud)

我通常更喜欢 Perl,sed原因如下:

  • Perl提供了完整的通用编程环境,但也sed有较多限制。
  • Perl 在CPAN上有一个巨大的公开可用模块存储库,可以轻松安装,然后与-M{module}选项一起使用。sed是不可扩展的。
  • Perl 拥有比 sed 更强大的正则表达式引擎,具有环顾断言、回溯控制动词、正则表达式内和替换 Perl 代码、更多选项和特殊转义、嵌入式组选项等。参见佩尔雷
  • 与直觉相反,尽管 Perl 更加复杂,但由于sed其两遍过程和高度优化的操作码实现,Perl 的速度通常要快得多。例如,请参阅http://rc3.org/2014/08/28/surprisingly-perl-outperforms-sed-and-awk/ 。
  • 我经常发现等效的 Perl 实现比 的更直观sed,因为sed有一组更原始的命令用于操作底层文本。