【问题标题】:Bash, Remove empty XML tagsBash,删除空的 XML 标记
【发布时间】:2014-12-31 09:02:34
【问题描述】:

我需要几个问题的帮助,使用 bash 工具

  1. 我想从文件中删除空的 xml 标签,例如:
 <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

这不是那么严格,它更像是一个技巧,更合适的是找到&lt;pattern&gt;&lt;/pattern&gt;并将其删除。建议?

  1. 二、怎么走:
 <CreateOfficeGroup>
       <CreateOfficeName>John</CreateOfficeName>
       <CreateOfficeCode>
       </CreateOfficeCode>
 </CreateOfficeGroup>

到:

 <CreateOfficeGroup>
       <CreateOfficeName>John</CreateOfficeName>
 </CreateOfficeGroup>
  1. 作为一个整体?来自:
 <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>

您能以个人身份回答这些问题吗?非常感谢!

【问题讨论】:

  • 不要为此使用 bash/string 处理。使用用于处理 XML 的工具。类似于 XSLT 或真正的 XML 解析器和一种语言的脚本,可让您遍历生成的文档并删除元素。
  • @CharlesDuffy 的真实故事,这里没有痛苦 :) 但还有一个原因是 sed 在 unix 系统上广泛可用,而这样的工具很可能必须安装。
  • @GézaTörök,可以肯定的是,但是因为sed 不知道 XML 语法,任何使用 sed 修改 XML 的尝试都注定是不正确的。它不知道 CDATA 部分中存在或不存在什么,它不知道评论中存在或不存在什么……您根本无法使用 sed 准确解析 XML。
  • @thahgr:你有现代的 Python 解释器吗?如果是这样,那包括一个适当的 XML 解析器。
  • 或者,您的系统是否有xsltproc

标签: xml linux bash sed


【解决方案1】:

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())

【讨论】:

    【解决方案2】:
    sed '#n
    1h;1!H
    $ { x
    :remtag
      s#\(\n* *\)*<\([^>]*>\)\( *\n*\)*</\2##g
      t remtag
    
      p
      }' YourFile
    

    (posix 版本所以--posix 在 GNU sed 上)

    • 从下杆到上杆递归删除空标签,直到不再出现空标签。
    • 不是 XML 解析器,所以像 &lt;tag1 prop="&lt;tag2&gt;&lt;/tag2&gt;"&gt; ... 这样的东西也会删除道具内容以及 xml 允许的任何其他类似的东西。

    【讨论】:

    • 我非常反对任何使用非语法感知工具来解析/生成结构化内容(只是因为有人不期望得到极端情况错误并不意味着这些极端情况不能在安全漏洞中被利用,或者不会导致某人失去一天的工作效率来追踪一个几乎不相关的组件中的一个错误,该组件在一年后调用了这个东西) ...但即使我也需要给这个+1。
    • 我同意,但有时不允许使用“基本工具”以外的其他工具(这是我的情况),并且在了解限制的情况下使用一些非 XML 解析器是短期内唯一的解决方案。从长远来看,考虑适应的工具(但不幸的是,解决方案通常是暂时的长期......)
    • 我在导入一个在其他工具上工作的 xml 文件时遇到了几千个错误,并且不知道有多少问题。这很快就完成了工作,消除了混乱。我可以快速找到剩余的问题并验证原始导入工具。对于我的用例,您的 sed 是一个完美的发现。谢谢!
    【解决方案3】:

    您可以使用 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 - 分支到 a如果没有到达最后一行)

    第一个替换的模式由开始标签 (&lt;\([^&gt;]*\)&gt;) - 可选空格 ([ \t\n]*) - 结束标签 (&lt;\/\1&gt;) 组成。请注意标记名称模式周围的转义括号,其内容可以在表达式中称为 \1。这就是结束标签匹配开始标签的方式。

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

    【讨论】:

    • 这似乎根本不适用于这里的测试。它似乎也没有处理带有属性的元素(假设它首先工作)。输入中没有任何内容,但对于实际数据可能不成立。这似乎也无法处理&lt;tag&gt;\n&lt;/tag&gt;(尽管在该空白类中包含\n,因为sed 是面向行的。
    • 现在留下空白行。现在你明白我关于为此使用 XML 工具的观点了吗? =)
    • 好吧,假设我确信 :)
    猜你喜欢
    • 1970-01-01
    • 2010-12-13
    • 2012-02-22
    • 2011-10-18
    • 1970-01-01
    • 2023-01-19
    • 2015-02-28
    • 2021-09-13
    • 2020-06-25
    相关资源
    最近更新 更多