只打印grep

时间:2016-08-30 15:27:17

标签: linux shell unix 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"; }

我们假设所有配置只占用一行,即使dhcpd.conf语法允许将选项分解为多行。但是,我们假设选项的顺序可能不同。

我想出了以下grep命令:

grep -o "^[^#]*host.*${DHCP_HOSTNAME}.*hardware ethernet.*..:..:..:..:..:..;" /etc/dhcp/dhcpd-hosts.conf

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

host client1 { hardware ethernet 12:23:34:56:78:89;

太好了!但重点是我只需要一个MAC地址,而不需要前面的垃圾箱。现在我知道使用另一个grep,或者cut或awk来从这个输出中仅删除MAC地址将是微不足道的。但我想知道,有没有办法使用单个grep命令来获得最终结果,而不必将此输出传输到另一个过滤器?显然,我不能忽略模式的开头,因为我想获得一个特定的主机名,从而匹配" ..:..:..:..:..:.."会给我所有的MAC地址。

再次,我想要一个命令(不一定是grep),它只从文件中删除正确的MAC地址。因此,我对那些说" grep ... |的任何解决方案都不感兴趣grep ..."或" grep ... |切..."等..

当然,在实践中,如果我使用多个过滤器并管道它们会发生什么不好的事情,我只是好奇是否有可能用一个过滤器来解决。

我会将输出分配给变量。

4 个答案:

答案 0 :(得分:2)

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

有几种方法可以将Perl用于此任务。我建议使用perl -ne {program}成语,它隐含地循环stdin行,并为每一行执行一行{program}一次,当前行作为$_特殊可用变量。 (注意:-n选项 not 导致$_的最终值在隐式循环的每次迭代结束时自动打印,这就是{{ 1}}选项可以;即-p。)

以下是解决方案。请注意,我决定使用晦涩的perl -pe {program}选项传递目标主机名,这样可以在-s参数之后解析变量赋值规范,类似于awk的{program}选项。 (使用-v选项传递正常的命令行参数是不可能的,因为隐式-n循环吞噬了文件名的所有这些参数,但while (<>) { ... }机制提供了一个很好的解决方案请参阅Is it possible to pass command-line arguments to @ARGV when using the -n or -p options?。)这种设计可以防止在-s字符串本身中嵌入$DHCP_HOSTNAME变量,这样我们就可以单引号并保存一些(实际上是8个)反斜杠

{program}

由于以下原因,我经常更喜欢Perl到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

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

答案 1 :(得分:1)

我会为此选择sed,因为你可以使用regexp进行行寻址:

sed -e "/host  *${DHCP_HOSTNAME}/!d" -e "s/*.\(hardware [^;]*\).*/\1/g"

第一个表达式删除所有不匹配${DHCP_HOSTNAME}的行(如果您的主机名中可能有任何正则表达式元字符,您可能希望在shell中按下它,但我假设您没有)。

第二个表达式与硬件地址部分匹配,并删除该行的其余部分。

答案 2 :(得分:0)

您可以使用以下表达式尝试Grep -o:

> typeof(infrastructure_data_frames)
[1] "list"
> typeof(infrastructure_data_frames[1])
[1] "list"

输出:

12:23:34:56:78:89
23:34:45:56:67:78
AB:CD:EF:01:23:45
C1:CA:88:FA:F4:90

上面的表达式将仅返回dhcp配置文件中的MAC地址。

答案 3 :(得分:0)

由于人们也使用不同的工具回答,我认为awk也可能是一个不错的选择。

$ cat so
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 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"; }
$ awk '/^[^#]/ && /client3/ { printf ("%s: %s\n",  $2, $6); }' so
client3: AB:CD:EF:01:23:45;

我使用双匹配来排除注释行,只需使用字段索引打印出想要的信息。这样,移除PXE部件也应该很容易。例如,删除host3的filename指令可以完成如下:

$ awk '/^[^#]/ && /client3/ { gsub(/filename[^;]+;/, ""); print; }' so
host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13;  }

指定自定义图像(pxecustom.0):

$ awk '/^[^#]/ && /client3/ { gsub(/filename[^;]+;/, "filename \"pxecustom.0\";"); print; }' so
host client3 { hardware ethernet AB:CD:EF:01:23:45; fixed-address 192.168.1.13; filename "pxecustom.0"; }