【问题标题】:Why does this pattern fail in awk?为什么这种模式在 awk 中会失败?
【发布时间】:2020-05-05 05:21:37
【问题描述】:

我尝试打印捕获的组,但awk 未能捕获它。我的正则表达式语法似乎没有错。有什么我错过的吗?

这是正文:

<key>NetworkServices</key>
<dict>
    <key>44412617dfsretret44rewtrtA2</key>
    <dict>
        <key>com.fdgfdgfg.ew3rer.gggfgfdgfg</key>
        <dict>
            <key>endpointProtocols</key>
            <array>
                <string>UDP:443:1450</string>
                <string>TCP:443:1450</string>
            </array>
        </dict>
        <key>DNS</key>
        <dict>
            <key>__INACTIVE__</key>
            <true/>
        </dict>
        <key>UserDefinedName</key>
        <string>4ghgfggfddg</string>
        <key>IPv6</key>
        <dict>
            <key>ConfigMethod</key>
            <string>Automatic</string>
        </dict>
        <key>Interface</key>
        <dict>
            <key>Type</key>
            <string>VPN</string>
        </dict>
        <key>__INACTIVE__</key>
        <true/>
        <key>VPN</key>
        <dict>
            <key>DisconnectOnIdle</key>
            <integer>0</integer>
        </dict>
        <key>IPv4</key>
        <dict>
            <key>ConfigMethod</key>
            <string>VPN</string>
        </dict>
        <key>Proxies</key>
        <dict>
            <key>__INACTIVE__</key>
            <true/>
            <key>FTPPassive</key>
            <integer>1</integer>
        </dict>
    </dict>
    <key>DF6rftr34354tergsdfsdf1D9</key>
    <dict>
        <key>Interface</key>
        <dict>
            <key>Type</key>
            <string>fgfdgfgfgr</string>
            <key>Hardware</key>
            <string>cfghfhgrCenter</string>
            <key>DeviceName</key>
            <string>ip1</string>

这是我的代码:

cat /var/text.txt |  awk 'match($0, /<key>NetworkServices<\/key>.*<key>(.*)<\/key>.*<key>Interface<\/key>.*<\/key>.*<string>ip1<\/string>/) {print substr($0, RSTART+1, RLENGTH-1)}'

它应该打印DF6rftr34354tergsdfsdf1D9,但它没有。

提前致谢。

这是经过测试的屏幕截图:

【问题讨论】:

  • 能否告诉我们您要打印哪个&lt;key&gt; 块,它的条件是什么?
  • 您使用的是哪个版本的 Awk? regular expressions 上的 GNU Awk 手册似乎没有提到捕获组 - 但 string manipulation functions 中的 gensub 示例显示它们在工作。这是一个 GNU 扩展功能;这意味着 subgsub 不这样做。
  • 另外,请记住,默认情况下,Awk 在单行上工作。
  • @JonathanLeffler gawk 家伙决定离开 sub() 和 gsub() POSIX 兼容,以便向后兼容和移植到其他 awk 变体,这就是为什么他们为捕获组引入 gensub() 并能够对字符串字面量进行操作并返回修改后的字符串(即类似 sed 的 sub() 函数)。他们还引入了非常有用的第三个参数来 match() 以使用捕获组填充数组。

标签: regex xml awk


【解决方案1】:

需要解决两个问题:模式应该与完整的文档匹配,并且模式使用组来选择值。

第一个问题可以通过指定数据中不存在的模式来解决。在这种情况下使用“”。请参阅Awk to read file as a whole 了解更多信息。

第二个问题是 RSTART、RLENGTH 捕获有关完整匹配的信息。鉴于匹配的模式是由一个组(在本例中为组 1)指定的,因此需要匹配的 3 参数版本,并且应该从组 1 数据中选择起始长度值。示例解决方案使用a 数组来捕获组信息。

这仅适用于 GNU awk。

cat /var/text.txt |  awk RS='<>' '
match($0, /<key>NetworkServices<\/key>.*<key>(.*)<\/key>.*<key>Interface<\/key>.*<\/key>.*<string>ip1<\/string>/, a) {
    print substr($0, a[1, "start"], a[1, "length"]) 
}'

作为旁注,建议 (1) 将每个 dict 键/值对存储到单个 XML 元素中,以及 (2) 使用 XML 工具解析整个文档。这将使任务更容易。例如,对映射条目使用 Java 表示法:

<dict>
   <entry>
      <key> key </key>
      <dict>
     </dict>
   </entry>
<dict>

【讨论】:

  • 很好的答案!谢谢!虽然我必须稍微修改一下线路才能在我的环境中工作:cat /var/text.txt | awk 'BEGIN {RS="&lt;&gt;";} match($0, /&lt;key&gt;NetworkServices&lt;\/key&gt;.*&lt;key&gt;(.*)&lt;\/key&gt;.*&lt;key&gt;Interface&lt;\/key&gt;.*&lt;\/key&gt;.*&lt;string&gt;ip1&lt;\/string&gt;/, a) { print substr($0, a[1, "start"], a[1, "length"]) }'
【解决方案2】:

我不认为 awk 是执行此操作的正确工具,因为它旨在逐行工作。由于您的模式跨越多行并且您有一个 XML 文件,因此请利用 XML 的强大结构。

所以,如果要打印满足某些条件的标记下的文本节点,请使用 xpath 表达式选择它。

根据您上面编写的正则表达式,我假设您正在寻找包含文本“NetworkServices”的标签&lt;key&gt;,然后您转到下一个节点,标签为&lt;dict&gt;,然后您会找到@987654323 @您要查找的节点,记住文本(这是您要查找的内容)并验证它后面是否有&lt;key&gt;Interface&lt;/key&gt;,然后转到标记为&lt;dict&gt; 的下一个节点,您必须在其中检查是否存在一个节点&lt;key&gt;DeviceName&lt;/key&gt;,后跟&lt;string&gt;ip1&lt;/string&gt;

这是我将使用 xpath 进行选择的代码:

/usr/bin/xpath -q -e '
  //key[text()="NetworkServices"]/following-sibling::dict[1]
    /key[
      following-sibling::dict[1]/key[
        text()="Interface" and 
        following-sibling::dict[1]/key[
          (text()="DeviceName") and (following-sibling::string[1]/text()="ip1")
        ]
      ]
    ]
    /text()
' input.xml

【讨论】:

  • it is aimed to work line by line - 不,awk 逐条记录。换行符只是默认的记录分隔符,但您可以轻松地重新定义它以对多行记录进行操作。我并不反对您应该使用支持 xml 的工具,只是关于 awk 的特定声明。如果有人关心的话,还有一个 GNU awk XML 库。
  • @EdMorton:确切地说,我的意思是 awk 旨在逐条记录工作。但是,如果您只有一条记录,那么您没有任何附加价值可以将 awk 用作包装器来执行正则表达式搜索。
  • 相对于做什么?举个例子——如果你的匹配字符串是多行,那么你不能使用 grep,也不能使用 POSIX sed,所以即使文件中只有一条记录,awk 也是最适合的。您还可以从使用 match 或 tolower/upper 或 awk 提供的其他几个函数中受益。
猜你喜欢
  • 2016-02-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-02-01
  • 1970-01-01
  • 1970-01-01
  • 2022-10-05
  • 2013-01-17
相关资源
最近更新 更多