mac-hosts
我有一个包含 MAC 地址及其关联主机名的文件:
e4:5f:01:21:79:01 PF3
e4:5f:01:21:79:03 PF3-BR0
e4:5f:01:21:79:be PF2
e4:5f:01:21:79:c0 PF2-BR0
我想计算具有正确格式的 MAC 地址和主机名的行数,我使用以下表达式:
FILTERED=$(cat mac-hosts | grep -P -c '/^[a-f0-9]{2}([:-])([a-f0-9]{2}\1){4}[a-f0-9]{2} [a-z0-9]*([-][a-z0-9]*)?$/i')
在这个表达式的每个版本中,我都会得到FILTERED = 0
一个结果。
我验证了https://regex101.com/文件的每一行都mac-hosts
正确匹配过滤器表达式,在提供的每种风格中都没有错误或警告,但 GoLang 和 Rust 除外,因为后向引用没有意义。我还研究了该man
页面grep
,但找不到我的过滤器不起作用的原因。
如果没有-P
,grep: Invalid back reference
我就知道正在使用 Perl 兼容的正则表达式语法。
我首先发现此故障发生在运行最新版本 Linux 版本的 Raspberry Pi 4B 上。
pi@PF2:~ $ uname -a
Linux PF2 6.1.21-v8+ #1642 SMP PREEMPT Mon Apr 3 17:24:16 BST 2023 aarch64 GNU/Linux
pi@PF2:~ $ grep -V
grep (GNU grep) 3.6
Copyright (C) 2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <https://gnu.org/licenses/gpl.html>.
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Written by Mike Haertel and others; see
<https://git.sv.gnu.org/cgit/grep.git/tree/AUTHORS>.
git-bash
此后,我在Windows 10 下运行时观察到了相同的行为。
我该如何调试这个问题并得到预期的结果,FILTERED = 4
结果在哪里?
更新
感谢您的回复,当我看到答案时很明显:我一直在考虑需要分隔斜杠的环境,而不是匹配字符串的一部分,并且i
是“忽略大小写”标志。对于命令行grep
,不使用分隔符,并且“忽略大小写”由开关设置-i
:
FILTERED=$(grep -Pic '^[a-f0-9]{2}([:-])([a-f0-9]{2}\1){4}[a-f0-9]{2} [a-z0-9]*([-][a-z0-9]*)?$' mac-hosts)
更新2
我仍然对主机名有问题不是有第二部分(连字符和更多的alm)。事实证明,这些名称末尾有空格(毫不奇怪)我在屏幕上没有看到。我在匹配字符串中添加了另一个组件来查找任何尾随空格。最终测试现在可以正常工作:
FILTERED=$(grep -Pic '^[a-f0-9]{2}([:-])([a-f0-9]{2}\1){4}[a-f0-9]{2} [a-z0-9]*([-][a-z0-9]*)?[[:space:]]$' mac-hosts)
我回滚了一个建议的编辑,其中作者删除了行结尾的测试。但是,这不会过滤掉允许的无效行,例如,主机名后面的标点符号在此格式中不允许。
答案1
至于为什么您的 grep 与文件中的行不匹配,乍一看,您的正则表达式有一个前导斜杠 ( /
) 字符和尾随字符 ( /i
) 未出现在文件的行中,因此没有匹配的行。在 Perl 脚本中,正/
则表达式的开头和结尾(以及结尾斜杠后面的修饰符)是与正则表达式相关的分隔符和修饰符,而不是表达式本身的一部分。
我建议两件事:
- 使用 POSIX 字符类创建更简单的表达式来匹配 MAC 地址和主机名。
grep
在不带 -c 选项的命令行上测试您的命令,并将其输出发送到终端窗口,直到正则表达式匹配正确的行。如果正确,请添加 -c 并验证计数。仅在测试成功后,才将输出捕获到脚本中的变量中。
这个命令对我使用安装在 Ubuntu 20.04 机器上的 GNU grep 有效:
grep -E '^[[:xdigit:]:]+ +[[:alnum:]_-]+' mac-hosts
请注意,grep
可以直接从文件中读取,无需调用cat
并将其通过管道传输到grep
.
此扩展正则表达式(由 -E 选项启用)匹配两个由空格字符分隔的可打印字符“字段”。第一个字段位于行的开头,由一个或多个十六进制字符或冒号 ( :
) 字符组成。第二个字段由一个或多个字母数字字符、下划线 ( _
) 或破折号 ( -
) 字符组成。
这并不强制第一个字段恰好具有由冒号分隔的两个十六进制字符的六个字段,也不强制第二个字段不仅仅是破折号或下划线而没有任何字母数字字符(或者仅存在大写字母),但表达式远,更容易理解和定制新的用例。
我将使用上面的内容过滤掉严重不正确的文件行(例如,缺少主机名),然后编写子例程对“好”行上的字段执行更严格的语法检查。如果你关心的话,那就是。在我自己的脚本中,上述内容足以完成任务,无需编写额外的语法检查子例程。
在调整正则表达式以匹配它应该匹配的行后,您可以添加 -c 选项来输出匹配行的计数而不是行本身。我建议对选项和参数进行排序,而grep -c -E
不是grep -E -c
让 -E 正则表达式选项与命令行上的表达式放在一起。从技术上讲是不必要的,但是像这样的小事情可能会对阅读脚本以理解/更新它的人有所帮助。 (这通常是几个月后的你)
如果您更喜欢 Perl 表达式,该命令对我来说适用 -P 而不是 -E。