【问题标题】:how to edit XML using bash script?如何使用 bash 脚本编辑 XML?
【发布时间】:2016-04-04 06:56:42
【问题描述】:
<root>
<tag>1</tag>
<tag1>2</tag1>
</root>

需要从 bash 更改值 1 和 2

【问题讨论】:

  • 把它们改成什么?如果这是你的输入,你想要的输出是什么?
  • 更改全局变量的值
  • sed 的解决方案:sed 's#([^#SOMETHING#'test.xml -i
  • @StefanoBorini,这有什么关系?大量可从 bash 访问的非正则表达式 XML 操作工具。
  • @Roman,如果您不关心正确性,那只是一个“解决方案”。例如,CDATA 部分中的&lt;tag&gt; 根本不是标签,而是文本; &lt;tag&gt; 里面的 &lt;----&gt; 是评论。在具有xmlns=http://example.com/foo 的子树下的&lt;tag&gt;{http://example.com/foo}tag,而不是tag。没有 sed 表达式会知道 XML 语法的复杂性。

标签: xml parsing bash sh


【解决方案1】:

tag 的值更改为2 并将tag1 的值更改为3,使用XMLStarlet

xmlstarlet ed \
  -u '/root/tag' -v 2 \
  -u '/root/tag1' -v 3 \
  <old.xml >new.xml

使用您的示例输入:

xmlstarlet ed \
  -u '/root/tag' -v 2 \
  -u '/root/tag1' -v 3 \
  <<<'<root><tag>1</tag><tag1>2</tag1></root>'

...作为输出发射:

<?xml version="1.0"?>
<root>
  <tag>2</tag>
  <tag1>3</tag1>
</root>

【讨论】:

  • 我刚刚检查过,这个工具可以通过apt-get install xmlstarlet 在 Debian(甚至从 Debian Jessie 开始)和 Ubuntu 上轻松安装。
【解决方案2】:

您可以将 xsltproc 命令(来自基于 Debian 的发行版上的包 xsltproc)与以下 XSLT 表一起使用:

<?xml version="1.0" encoding="UTF-8"?>
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform">
  <xsl:output method="xml" indent="yes"/>
  <xsl:param name="tagReplacement"/>
  <xsl:param name="tag1Replacement"/>

  <xsl:template match="node()|@*">
    <xsl:copy>
      <xsl:apply-templates select="node()|@*"/>
    </xsl:copy>

  </xsl:template>
  <xsl:template match="tag">
    <xsl:copy>
      <xsl:value-of select="$tagReplacement"/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="tag1">
    <xsl:copy>
      <xsl:value-of select="$tag1Replacement"/>
    </xsl:copy>
  </xsl:template>
</xsl:stylesheet>

然后使用命令:

xsltproc --stringparam tagReplacement polop \
         --stringparam tag1Replacement palap \
         transform.xsl input.xml

或者你也可以使用正则表达式,但是通过正则表达式修改 XML 是纯粹的恶 :)

【讨论】:

    【解决方案3】:

    我在 python 中的 0.02 美元,因为它在您将登录的每台服务器上

    import sys, xml.etree.ElementTree as ET
    
    data = ""
    for line in sys.stdin:
        data += line
    
    tree = ET.fromstring(data)
    
    nodeA = tree.find('.//tag')
    nodeB = tree.find('.//tag1')
    
    tmp = nodeA.text
    nodeA.text = nodeB.text
    nodeB.text = tmp 
    
    print ET.tostring(tree)
    

    这是从标准输入读取的,因此您可以像这样使用它:

    $ echo '<node><tag1>hi!</tag1><tag>this</tag></node>' | python xml_process.py 
    <node><tag1>this</tag1><tag>hi!</tag></node>
    

    编辑 - 接受挑战

    这是一个有效的 xmllib 实现(应该可以追溯到 python 1.6)。因为我认为用叉子刺我的眼睛会更有趣。我要说的唯一想法是它适用于给定的用例。

    import sys, xmllib
    
    class Bag:
        pass
    
    class NodeSwapper(xmllib.XMLParser):
        def __init__(self):
        print 'making a NodeSwapper'
        xmllib.XMLParser.__init__(self)
        self.result = ''
        self.data_tags = {}
        self.current_tag = ''
        self.finished = False
    
        def handle_data(self, data):
        print 'data: ' + data
    
        self.data_tags[self.current_tag] = data
        if self.finished:
           return
    
        if 'tag1' in self.data_tags.keys() and 'tag' in self.data_tags.keys():
            b = Bag()
            b.tag1 = self.data_tags['tag1']
            b.tag = self.data_tags['tag']
            b.t1_start_idx = self.rawdata.find(b.tag1)
            b.t1_end_idx = len(b.tag1) + b.t1_start_idx
            b.t_start_idx = self.rawdata.find(b.tag)
            b.t_end_idx = len(b.tag) +  b.t_start_idx 
            # swap
            if b.t1_start_idx < b.t_start_idx:
               self.result = self.rawdata[:b.t_start_idx] + b.tag + self.rawdata[b.t_end_idx:]
               self.result = self.result[:b.t1_start_idx] + b.tag1 + self.result[b.t1_end_idx:]
            else:
               self.result = self.rawdata[:b.t1_start_idx] + b.tag1 + self.rawdata[t1_end_idx:]
               self.result = self.result[:b.t_start_idx] + b.tag + self.rresult[t_end_idx:]
            self.finished = True
    
        def unknown_starttag(self, tag, attrs):
        print 'starttag is: ' + tag
        self.current_tag = tag
    
    data = ""
    for line in sys.stdin:
        data += line
    
    print 'data is: ' + data
    
    parser = NodeSwapper()
    parser.feed(data)
    print parser.result
    parser.close()
    

    【讨论】:

    • Python 无处不在,是的。 Python 足够新,可以在标准库中包含 ElementTree……这有点不确定。
    • 太真实了。但我宁愿用叉子刺自己的眼睛,也不愿尝试用 xmllib 可靠地做到这一点。实际上这是使用 ruby​​ 的一个很好的理由(除非你碰巧使用了 solaris 或 HP-UX 机器,在这种情况下我们最终选择了 perl)
    • 好吧。经过那种程度的努力,我不能投赞成票。 :)
    【解决方案4】:

    既然您在其中一个 cmets 中给出了 sed 示例,我想您想要一个纯 bash 解决方案?

    while read input; do
      for field in tag tag1; do
        case $input in
          *"<$field>"*"</$field>"* )
            pre=${input#*"<$field>"}
            suf=${input%"</$field>"*}
            # Where are we supposed to be getting the replacement text from?
            input="${input%$pre}SOMETHING${input#$suf}"
            ;;
        esac
      done
      echo "$input"
    done
    

    这完全不智能,显然只适用于格式良好的输入,开始标签和结束标签在同一行,同一行不能有多个相同标签的实例,标签列表替换是硬编码的,等等。

    我无法想象这会真正有用,并且比脚本或适当的 XML 方法更可取。

    【讨论】:

      猜你喜欢
      • 2010-12-05
      • 2014-08-29
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-03-23
      • 1970-01-01
      • 2023-04-03
      • 1970-01-01
      相关资源
      最近更新 更多