【问题标题】:sed - regex address range is matching lines outside the range when N command is usedsed - 使用 N 命令时,正则表达式地址范围匹配范围外的行
【发布时间】:2018-02-20 17:48:49
【问题描述】:

问题描述:

考虑下面的 XML 文件:

<xmlhead1>
   <xmlsubhead1>
       <record>
           <field>Hello</field>
           <field>World</field>
       </record>
       <record>
           <field>DELETEKEY</field>
           <field>World1</field>
       </record>
   </xmlsubhead1>
</xmlhead1>

我的目标是删除一个“记录”XML标签,当字段子标签 该 XML 节点的 包含 DELETEKEY 作为值。

所以在上面的 XML 文件中我将删除

   <record>
       <field>DELETEKEY</field>
       <field>World1</field>
   </record>

选择的解决方案: 我尝试使用 GNU sed 来解决上述问题:
下面是我的代码。

sed -n '

/<xmlhead1>/,/<\/xmlhead1>/{
    /<xmlsubhead1>/,/<\/xmlsubhead1>/{
        /<record>/,/<\/record>/{

            #Append to hold space
            H

            #if match DELETEKEY, start delete processing for the xml <record> element
            /<field>DELETEKEY<\/field>/{
                s/.*//g ; x
                b delete
            }

            #if you have reached the end tag of the <record> element,
            #print the hold space and clear the buffers
            /<\/record>/{
                g ; s/^\n//g; p
                s/.*//g ; x ; s/.*//g
            }

            #continue to next line
            b

            #delete processing
            :delete
            {
                #clear pattern space.
                s/.*//g

                #Read Next Line and remove new line(\n)
                N
                s/^\n//g

                #end delete processing when line matches the end tag </record>
                /<\/record>/b

                #else continue to get next line for delete process
                b delete
            }
        }
    }
}

#print all other lines
p

' $inputfile

逻辑如下:

  1. 匹配以<xmlhead1> and ending with </xmlhead1>开头的地址范围
  2. 匹配内部地址范围以 &lt;xmlsubhead1&gt; and ending with &lt;/xmlsubhead1&gt;
  3. 匹配内部地址范围&lt;record&gt; to &lt;/record&gt;
  4. 当在&lt;record&gt;标签内时,
    (i) 将所有行追加到保持空间。
    (ii) 如果该行匹配DELETEKEY,则必须删除该记录。执行步骤 iii 和 iv。否则,如果没有匹配,请转到步骤 v
    (iii)对于delete,清空hold space并跳转到delete分支
    (iv) 在删除分支中,使用“N”命令读取所有下一行,直到遇到&lt;/record&gt;。当&lt;/record&gt; 是 遇到,退出循环,开始处理下一个 线。
    (v) 不处理删除逻辑时,如果遇到&lt;/record&gt;,则表示&lt;record&gt; to &lt;/record&gt;的块是 已成功处理并存在于保留空间中。
    (vi) 所以把它从存放空间中取出并打印出来。

上述逻辑的输出:

<xmlhead1>
   <xmlsubhead1>
       <record>
           <field>Hello</field>
           <field>World</field>
       </record>
</xmlhead1>

输出中的问题:
您会注意到带有 DELETEKEY 的记录元素已被删除,但输出中缺少 &lt;/xmlsubhead1&gt; 标记。

问题调试:
在调试时我发现在删除处理中遇到&lt;/record&gt; 行后,在&lt;record&gt; to &lt;/record&gt; 范围内,内部地址范围匹配应该已经结束,因为我已经读取并处理了&lt;/record&gt; 行。

但&lt;record&gt; to &lt;/record&gt; 范围块似乎也处理&lt;/xmlsubhead1&gt; 行。

我通过在&lt;record&gt; 范围的命令块中添加以下代码发现了这一点。

/<record>/,/<\/record>/{

    /<\/recordList>/{
        s/.*/record list is inside the record to record range/g
        p
    }

有人可以解释 sed 的这种行为,即范围匹配超出了实际匹配吗?在这种情况下&lt;record&gt; to &lt;/record&gt; range match is also matching &lt;/xmlsubhead1&gt;

【问题讨论】:

  • 你使用正则表达式而不是像xmlstarlet这样的正确的XML感知工具有什么原因吗?
  • 没有理由。我只是想在 sed 中实现并偶然发现了上述行为。似乎很奇怪。虽然稍后我将使用其他工具来实际解决问题,但我想知道为什么 sed 的行为似乎如上所述。

标签: sed text-processing


【解决方案1】:

不要使用 sed 来编辑 XML,使用 XML 感知工具。比如xsh,可以写:

open file.xml ;
delete //record[field="DELETEKEY"] ;
save :b ;

【讨论】:

  • 感谢您的建议,但我很想知道为什么 sed 会这样。
【解决方案2】:

我同意 cmets 关于使用适当的 XML 解析器的观点。

您的 sed 脚本的问题在于您在 :delete“函数”中读取了 (N) 行。这是一个使用更简单逻辑的工作示例:

/<xmlhead1>/,/<\/xmlhead1>/{
  /<xmlsubhead1>/,/<\/xmlsubhead1>/{
    /<record>/ {
      :a
      N
      /<\/record>/!ba
      /<field>DELETEKEY<\/field>/d
    }
  }
}
p

即在正确的上下文中,读取完整的记录(假设简化的 XML 结构),如果记录包含违规文本,则将其删除。

【讨论】:

  • " 您的 sed 脚本的问题是您在 :delete "function" " 中读取了 (N) 行,您能否详细说明为什么会出现问题?
  • @Abis:问题似乎是您阅读了&lt;/record&gt; 行和N,因此/&lt;record&gt;/,/&lt;\/record&gt;/ 范围永远不会终止。我认为
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-23
  • 1970-01-01
  • 2011-05-26
  • 2013-07-14
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多