Bash,删除空XML标记

时间:2014-11-04 12:56:57

标签: xml linux bash sed

我需要一些帮助,使用bash工具

  1. 我想从文件中删除空的xml标签,例如:
  2.  <CreateOfficeCode>
          <OperatorId>ve</OperatorId>
          <OfficeCode>1234</OfficeCode>
          <CountryCodeLength>0</CountryCodeLength>
          <AreaCodeLength>3</AreaCodeLength>
          <Attributes></Attributes>
          <ChargeArea></ChargeArea>
     </CreateOfficeCode>
    

    成为:

     <CreateOfficeCode>
          <OperatorId>ve</OperatorId>
          <OfficeCode>1234</OfficeCode>
          <CountryCodeLength>0</CountryCodeLength>
          <AreaCodeLength>3</AreaCodeLength>
     </CreateOfficeCode>
    

    为此我已通过此命令

    完成此操作
    sed -i '/><\//d' file
    

    这不是那么严格,它更像是一个技巧,更合适的是找到<pattern></pattern>并删除它。建议?

    1. 其次,如何前往:
    2.  <CreateOfficeGroup>
             <CreateOfficeName>John</CreateOfficeName>
             <CreateOfficeCode>
             </CreateOfficeCode>
       </CreateOfficeGroup>
      

      为:

       <CreateOfficeGroup>
             <CreateOfficeName>John</CreateOfficeName>
       </CreateOfficeGroup>
      
      1. 整体而言?从:
      2.  <CreateOfficeGroup>
               <CreateOfficeName>John</CreateOfficeName>
               <CreateOfficeCode>
                    <OperatorId>ve</OperatorId>
                    <OfficeCode>1234</OfficeCode>
                    <CountryCodeLength>0</CountryCodeLength>
                    <AreaCodeLength>3</AreaCodeLength>
                    <Attributes></Attributes>
                    <ChargeArea></ChargeArea>
               </CreateOfficeCode>
               <CreateOfficeSize>
                    <Chairs></Chairs>
                    <Tables></Tables>
               </CreateOfficeSize>
         </CreateOfficeGroup>
        

        为:

         <CreateOfficeGroup>
               <CreateOfficeName>John</CreateOfficeName>
               <CreateOfficeCode>
                    <OperatorId>ve</OperatorId>
                    <OfficeCode>1234</OfficeCode>
                    <CountryCodeLength>0</CountryCodeLength>
                    <AreaCodeLength>3</AreaCodeLength>
               </CreateOfficeCode>
         </CreateOfficeGroup>
        

        您能否以个人身份回答问题?非常感谢你!

3 个答案:

答案 0 :(得分:5)

XMLStarlet是一个命令行XML处理器。用它来做你想要的是一行操作(直到添加了所需的递归行为),并且适用于描述相同输入的所有XML语法变体:

简单版本:

xmlstarlet ed \
  -d '//*[not(./*) and (not(./text()) or normalize-space(./text())="")]' \
  input.xml

花哨的版本:

strip_recursively() {
  local doc last_doc
  IFS= read -r -d '' doc 
  while :; do
    last_doc=$doc
    doc=$(xmlstarlet ed \
           -d '//*[not(./*) and (not(./text()) or normalize-space(./text())="")]' \
           /dev/stdin <<<"$last_doc")
    if [[ $doc = "$last_doc" ]]; then
      printf '%s\n' "$doc"
      return
    fi
  done
}
strip_recursively <input.xml
使用

/dev/stdin而不是-(以平台可移植性为代价),以便在XMLStarlet版本之间实现更好的可移植性;适应味道。


如果系统只安装了较旧的依赖项,则安装的XML解析器更可能与Python捆绑在一起。

#!/usr/bin/env python

import xml.etree.ElementTree as etree
import sys

doc = etree.parse(sys.stdin)
def prune(parent):
    ever_changed = False
    while True:
        changed = False
        for el in parent.getchildren():
            if len(el.getchildren()) == 0:
                if ((el.text is None or el.text.strip() == '') and
                    (el.tail is None or el.tail.strip() == '')):
                    parent.remove(el)
                    changed = True
            else:
                changed = changed or prune(el)
        ever_changed = changed or ever_changed
        if changed is False:
            return ever_changed

prune(doc.getroot())
print etree.tostring(doc.getroot())

答案 1 :(得分:4)

sed '#n
1h;1!H
$ { x
:remtag
  s#\(\n* *\)*<\([^>]*>\)\( *\n*\)*</\2##g
  t remtag

  p
  }' YourFile

(在GNU sed上的posix版本--posix

  • 以递归方式将空标签从下部拉杆移至上部标签,直至不再出现空标签。
  • 不是XML解析器,所以像<tag1 prop="<tag2></tag2>"> ...这样的东西也会删除道具内容以及xml允许的任何其他内容。

答案 2 :(得分:2)

您可以使用sed执行以下操作:

sed -i ':a;N;$!ba;s/<\([^>]*\)>[ \t\n]*<\/\1>//g;s/\([\n][\t\n ]*[\n]\)/\n/g;' yourfile.xml

开头的脚本(:l;N;$!bl)通过循环将所有行连接到模式空间(:a - 声明标签a; N - 将下一行附加到模式空间; $!bl - branch到达最后一行的if)

第一次替换的模式就像开始标记(<\([^>]*\)>) - 可选的空格([ \t\n]*) - 结束标记(<\/\1>)一样。请注意标记名称模式周围的转义parens,其内容在表达式中可以称为\ 1。这就是结束标记与开始标记的匹配方式。

最后,第二次替换(s/[\n][\n]*/\n/g)只是删除了连续的换行符。