我目前正在尝试编写一个awk脚本来处理一堆DNS区域文件。但是,我遇到了一个问题,其中某些DNS区域记录(即TXT记录形式的SPF和DKIM记录)中有空格,这会导致awk将区域记录的数据解释为多个字段。但是,他们共享的一个一致特征是TXT记录的数据始终用双引号括起来,并且它始终是最后一个实际字段。
以下是一个示例DNS区域,其中包含几条SPF记录,仅用于显示输入文件的结构:
; cPanel first:11.34.1.7 (update_time):1380693490 Cpanel::ZoneFile::VERSION:1.3 hostname:bentley.websitewelcome.com latest:11.36.2.4
; Zone file for digdeepdns.net
$TTL 14400
digdeepdns.net. 86400 IN SOA ns1.digdeepdns.net. slucas.digdeepdns.net. (
2013100200 ;Serial Number
14400 ;refresh
7200 ;retry
3600000 ;expire
14400 ;minimum
)
digdeepdns.net. 86400 IN NS ns1.digdeepdns.net.
digdeepdns.net. 86400 IN NS ns2.digdeepdns.net.
digdeepdns.net. 14400 IN A 192.185.57.22
localhost 14400 IN A 127.0.0.1
digdeepdns.net. 14400 IN MX 0 digdeepdns.net.
mail 14400 IN CNAME digdeepdns.net.
www 14400 IN CNAME digdeepdns.net.
ftp 14400 IN A 192.185.57.22
digdeepdns.net. IN TXT "v=spf1 ip4:70.84.243.130 a mx ip4:192.185.57.216 include:websitewelcome.com ~all"
cpanel 14400 IN A 192.185.57.22
webmail 14400 IN A 192.185.57.22
whm 14400 IN A 192.185.57.22
webdisk 14400 IN A 192.185.57.22
ns1 14400 IN A 192.185.57.216
ns2 14400 IN A 192.185.57.22
hg 14400 IN A 192.185.57.22
www.hg 14400 IN A 192.185.57.22
hg IN TXT "v=spf1 ip4:70.84.243.130 +a +mx +ip4:192.185.57.216 ?all"
webdisk.hg IN A 192.185.57.22
每个记录的字段分隔符可以是制表符或空格字符串。您还可以看到并非所有记录都具有明确定义的TTL,因此我不能假设$ 2将是一个数字而$ 3将是文字'IN'。 SOA之后所有行中唯一的共同线程是$ 1,/[-_0-9a-z.]+/
可以轻松捕获。我不打算进行精确和验证;这些DNS区域是由一个脚本生成的,几乎可以保证它们符合RFC 1035标准。
DNS区域当前正由以下awk脚本处理:
#! /opt/local/bin/awk -f
BEGIN { OFS = "\t" }
NR < 11 { print }
NR > 10 && /("[^"]+")/ { print }
到目前为止,这是脚本的示例输出,遗憾的是忽略了每一行 - 除了 - 这两个是SPF记录,但至少证明正则表达式/("[^"]+")/
就像一个魅力:
; cPanel first:11.34.1.7 (update_time):1380693490 Cpanel::ZoneFile::VERSION:1.3 hostname:bentley.websitewelcome.com latest:11.36.2.4
; Zone file for digdeepdns.net
$TTL 14400
digdeepdns.net. 86400 IN SOA ns1.digdeepdns.net. slucas.digdeepdns.net. (
2013100200 ;Serial Number
14400 ;refresh
7200 ;retry
3600000 ;expire
14400 ;minimum
)
digdeepdns.net. IN TXT "v=spf1 ip4:70.84.243.130 a mx ip4:192.185.57.216 include:websitewelcome.com ~all"
hg IN TXT "v=spf1 ip4:70.84.243.130 +a +mx +ip4:192.185.57.216 ?all"
区域文件的前10行可以忽略(因此脚本直接打印出来);无论如何它们都需要一些手动处理。但是第11行和以后需要更好的对齐,我打算用printf来完成。
我首先开始简单地弄清楚我在做什么,但最终我将使用printf替换带有标签的空格,以便我可以使列更整齐地对齐。但是,由于IFS必须是空白,这就带来了挑战,即我的DNS区域中少数TXT记录中的文本数据必须以某种方式全局整合并解释为与printf一起使用的单个输入字段。因此,正则表达式可以捕获双引号内的整个文本。
我特意在awk中寻求解决方案(或者至少是一个非常有用的暗示,可以引导我找到解决方案),因为这种学习体验专门针对awk。我确信我可以找到一种可以在sed中轻松完成的方法,但这并不是我最终目标的重点。
我在这个问题上有点过头了,我真的可以帮忙。
编辑:
在推荐时,这里是一个所需输出的样本(表格可能有点笨拙,但基本要点是第1列有3个选项卡空间,所有其他选项卡有1个选项卡):
; cPanel first:11.34.1.7 (update_time):1380693490 Cpanel::ZoneFile::VERSION:1.3 hostname:bentley.websitewelcome.com latest:11.36.2.4
; Zone file for digdeepdns.net
$TTL 14400
digdeepdns.net. 86400 IN SOA ns1.digdeepdns.net. slucas.digdeepdns.net. (
2013100200 ;Serial Number
14400 ;refresh
7200 ;retry
3600000 ;expire
14400 ;minimum
)
digdeepdns.net. 86400 IN NS ns1.digdeepdns.net.
digdeepdns.net. 86400 IN NS ns2.digdeepdns.net.
digdeepdns.net. 14400 IN A 192.185.57.22
localhost 14400 IN A 127.0.0.1
digdeepdns.net. 14400 IN MX 0 digdeepdns.net.
mail 14400 IN CNAME digdeepdns.net.
www 14400 IN CNAME digdeepdns.net.
ftp 14400 IN A 192.185.57.22
digdeepdns.net. IN TXT "v=spf1 ip4:70.84.243.130 a mx ip4:192.185.57.216 include:websitewelcome.com ~all"
cpanel 14400 IN A 192.185.57.22
webmail 14400 IN A 192.185.57.22
whm 14400 IN A 192.185.57.22
webdisk 14400 IN A 192.185.57.22
ns1 14400 IN A 192.185.57.216
ns2 14400 IN A 192.185.57.22
hg 14400 IN A 192.185.57.22
www.hg 14400 IN A 192.185.57.22
hg IN TXT "v=spf1 ip4:70.84.243.130 +a +mx +ip4:192.185.57.216 ?all"
webdisk.hg IN A 192.185.57.22
最终的问题是我如何让awk处理前3-4个字段(取决于$ 2是数字还是'IN')和正常情况一样,然后对于字段5+,它也需要是条件的。引号中的任何内容都必须作为单个字段处理。我非常喜欢建议或指示,以帮助我找到解决方案。
编辑#2:
在一个有些相关且同时无关的侧切线上。我似乎无法找到一个明确的答案,关于是否可以将模式{statement}对作为模式{statement}子句中的语句嵌套,如下所示:
patternA {
patternAA { statements }
patternAB { statements }
}
patternB {
patternBA { statements }
patternBB { statements }
}
这可能在awk中吗?如果是这样,这可能有助于我弄清楚如何做这个脚本。
答案 0 :(得分:1)
可能有更好的方法来写这个,但在这里:
NR < 11 { print; next }
{
hname = $1
if ($2 == "IN") {
port = ""
dom = $3
addri = 4
}
else {
port = $2
dom = $4
addri = 5
}
addr = $addri
if (dom == "TXT") {
for (i = addri + 1; i <= NF; i++) {
addr = addr" "$i
if ( index($i, "\"") != 0)
break
}
}
printf("%-30s\t%s\t%s\t%-10s\t%s\n", hname, port, "IN", dom, addr)
}
答案 1 :(得分:0)
#! /opt/local/bin/awk -f
BEGIN{FS=" IN "}
NR<11{print;next}
{ split($1,a,OFS);printf "%-30s%-10s\t%s\t",a[1],a[2],FS
l=split($2,b,OFS);printf "%-10s\t",b[1]
for (i=2;i<=l;i++) printf b[i] OFS;printf RS
}
$ awk -f b.awk infile
; cPanel first:11.34.1.7 (update_time):1380693490 Cpanel::ZoneFile::VERSION:1.3 hostname:bentley.websitewelcome.com latest:11.36.2.4
; Zone file for digdeepdns.net
$TTL 14400
digdeepdns.net. 86400 IN SOA ns1.digdeepdns.net. slucas.digdeepdns.net. (
2013100200 ;Serial Number
14400 ;refresh
7200 ;retry
3600000 ;expire
14400 ;minimum
)
digdeepdns.net. 86400 IN NS ns1.digdeepdns.net.
digdeepdns.net. 86400 IN NS ns2.digdeepdns.net.
digdeepdns.net. 14400 IN A 192.185.57.22
localhost 14400 IN A 127.0.0.1
digdeepdns.net. 14400 IN MX 0 digdeepdns.net.
mail 14400 IN CNAME digdeepdns.net.
www 14400 IN CNAME digdeepdns.net.
ftp 14400 IN A 192.185.57.22
digdeepdns.net. IN TXT "v=spf1 ip4:70.84.243.130 a mx ip4:192.185.57.216 include:websitewelcome.com ~all"
cpanel 14400 IN A 192.185.57.22
webmail 14400 IN A 192.185.57.22
whm 14400 IN A 192.185.57.22
webdisk 14400 IN A 192.185.57.22
ns1 14400 IN A 192.185.57.216
ns2 14400 IN A 192.185.57.22
hg 14400 IN A 192.185.57.22
www.hg 14400 IN A 192.185.57.22
hg IN TXT "v=spf1 ip4:70.84.243.130 +a +mx +ip4:192.185.57.216 ?all"
webdisk.hg IN A 192.185.57.22