【问题标题】:How to capture a string of text as a single field in awk?如何在awk中将一串文本捕获为单个字段?
【发布时间】:2013-12-16 13:23:24
【问题描述】:

我目前正在尝试编写一个 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:

在有点相关但同时不相关的侧切线上。关于是否可以将模式{语句}对嵌套为模式{语句}子句中的语句,我似乎无法找到明确的答案,如下所示:

patternA {
    patternAA { statements }
    patternAB { statements }
}
patternB {
    patternBA { statements }
    patternBB { statements }
}

这在 awk 中可行吗?如果是这样,那可能会帮助我弄清楚如何执行此脚本。

【问题讨论】:

  • 这里有很多东西。很难说你的终极问题是什么。鉴于上面的示例输入,您可以重新编辑您的问题以包含所需的输出吗?虽然很多人喜欢 awk 中的规则/动作对,但您可能会发现 awk '{ if (N$&lt;11) { print } else if (...) { something else } ; else { do this }}' input 形式的内容可能更适合您打印前 10 行的情况(例如)。祝你好运。
  • 您可以随时在 std /pattB/ { if($0 ~ patternBA) { ... } else if ($0 ~ patternBB) {...} else {... } } 块内嵌套测试模式!祝你好运。

标签: regex bash awk


【解决方案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)
}

【讨论】:

  • 这一行得到错误的输出:digdeepdns.net。 14400 IN MX 0
【解决方案2】:
#! /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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-04-26
    • 2019-06-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-03-17
    相关资源
    最近更新 更多