如何优化用于Plan-Server匹配检测的awk命令中server_name提取逻辑?
问题描述
现有data.txt数据内容如下:
+----------------------------+---------+-------------------+ | Hostname | Plan | Server | +----------------------------+---------+-------------------+ | hostname1 | B20C120 | R1SG7.myfqdn.com | | another-host-name | B40G66 | R1DC8.myfqdn.com | | the-other-hostname-is-here | B180G22 | R1SG8.myfqdn.com | | last.mydomain.com | B400C88 | R1DG10.myfqdn.com | +----------------------------+---------+-------------------+
使用以下AWK命令检测Plan与Server的匹配性(Plan以B开头且包含C/G,Server在R1S/R1D后包含C/G,二者C/G不匹配则输出告警):
awk -F '|' 'NF>3 { match($3, "B[0-9]{1,3}([CG])[0-9]{1,9}", plan); match($4, "R[0-9]{1,2}[DS]([CG])[0-9]{1,3}\.myfqdn\.com", server); match($2, "[a-zA-Z0-9 -.]{1,99}", server_name); if (plan[1] != server[1]) print "wrong server ==> plan:", plan[0] ",", "server:", server[0], ", server_name:" server_name[0] }' data.txt
当前输出符合预期:
wrong server ==> plan: B20C120, server: R1SG7.myfqdn.com, server_name: hostname1 wrong server ==> plan: B40G66, server: R1DC8.myfqdn.com, server_name: another-host-name wrong server ==> plan: B400C88, server: R1DG10.myfqdn.com, server_name: last.mydomain.com
但提取server_name时需要编写复杂正则,尝试简化时遇到问题:
- 使用
match($2, server_name);报错:
awk: cmd. line:6: (FILENAME=data.txt FNR=4) fatal: attempt to use scalar `server_name' as an array
- 使用
match($2, "(.*)", server_name);输出无变化,期望优化server_name的提取方式,无需复杂正则。
解决方案
方法1:用gsub去除字段前后空格
$2是被|分隔的字段,仅前后存在多余空白字符,直接清理即可得到纯净的Hostname:
awk -F '|' 'NF>3 { match($3, "B[0-9]{1,3}([CG])[0-9]{1,9}", plan); match($4, "R[0-9]{1,2}[DS]([CG])[0-9]{1,3}\.myfqdn\.com", server); server_name = $2; gsub(/^[[:space:]]+|[[:space:]]+$/, "", server_name); # 清除前后所有空白字符 if (plan[1] != server[1]) print "wrong server ==> plan:", plan[0] ",", "server:", server[0], ", server_name:", server_name }' data.txt
方法2:使用GNU AWK内置trim函数
如果使用的是GNU AWK(gawk),可以直接调用内置trim函数去除字符串前后空格:
awk -F '|' 'NF>3 { match($3, "B[0-9]{1,3}([CG])[0-9]{1,9}", plan); match($4, "R[0-9]{1,2}[DS]([CG])[0-9]{1,3}\.myfqdn\.com", server); server_name = trim($2); if (plan[1] != server[1]) print "wrong server ==> plan:", plan[0] ",", "server:", server[0], ", server_name:", server_name }' data.txt
方法3:简化match正则
如果坚持用match,可以用更简洁的正则匹配连续非空白字符(Hostname字段的有效内容就是中间的非空白序列):
awk -F '|' 'NF>3 { match($3, "B[0-9]{1,3}([CG])[0-9]{1,9}", plan); match($4, "R[0-9]{1,2}[DS]([CG])[0-9]{1,3}\.myfqdn\.com", server); match($2, "[^[:space:]]+", server_name); # 匹配第一个连续非空白字符序列 if (plan[1] != server[1]) print "wrong server ==> plan:", plan[0] ",", "server:", server[0], ", server_name:", server_name[0] }' data.txt
以上三种方法都能避免编写复杂的字符集正则,高效提取到纯净的Hostname内容。
内容的提问来源于stack exchange,提问作者Saeed
相关产品推荐
相关产品推荐

