我对是否可以在以下情况下使用单个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 ... |切..."等..
当然,在实践中,如果我使用多个过滤器并管道它们会发生什么不好的事情,我只是好奇是否有可能用一个过滤器来解决。
我会将输出分配给变量。
答案 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
:
sed
则更为有限。sed
选项一起使用。 -M{module}
不可扩展。sed
快得多。例如,请参阅http://rc3.org/2014/08/28/surprisingly-perl-outperforms-sed-and-awk/。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"; }