sed - 正则表达式地址范围是使用N命令时匹配范围之外的行

时间:2018-02-19 13:10:57

标签: sed text-processing

问题描述:

  

考虑以下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. 开头   
  3. 匹配以...开头的内部地址范围   <xmlsubhead1> and ending with </xmlsubhead1>
  4.   
  5. 匹配内部地址范围<record> to </record>
  6.   
  7. <record>标记内,
          (i)将所有线路附加到保留空间       (ii)如果该行匹配DELETEKEY,则必须删除该记录。执行步骤iii和iv。否则,如果没有匹配,请转到步骤v
          (iii)对于删除,清除保留空间并跳转到删除分支
          (iv)在删除分支中,使用“N”命令读取所有下一行,直到遇到</record>。当</record>时   遇到,退出循环并开始处理下一个   行。
          (v)当不处理删除逻辑时,如果遇到</record>,则表示<record> to </record>的块   成功处理并存在于保留空间中       (vi)将其从保持空间中取出并打印出来。
  8.   

输出上述逻辑:

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

输出中的问题:
您可以注意到删除了带有DELETEKEY的记录元素,但输出中缺少</xmlsubhead1>标记。

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

<record> to </record>范围块似乎也处理</xmlsubhead1>行。

我通过在<record>范围的命令块中添加以下代码找到了这个。

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

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

有人可以解释sed的这种行为,范围匹配超过实际匹配吗?在这种情况下<record> to </record> range match is also matching </xmlsubhead1>

2 个答案:

答案 0 :(得分:1)

不要使用sed编辑XML,请使用支持XML的工具。例如,在xsh中,您可以写:

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

答案 1 :(得分:0)

我同意有关使用正确的XML解析器的评论。

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

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

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