Bash

更改標籤中的文本,但只有標籤包含在某個 XML 塊中

  • June 16, 2015

使用 Git Bash,我試圖有條件地替換數百個文件中 yrot 標籤中的內容,但前提是它屬於與 wheel 相關的元件名稱標籤。

// YES, change

<part name="D_wheel1" seqNumber="1" >
 <yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>

// YES, change 

<part name="D_wheel2" seqNumber="1" >
 <yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>

// NO, don't change
<part name="door" seqNumber="1" >
 <yrot min="0.000000" max="0.000000" cur="0.000000" />
</part>

// Example Line Change
// From: <yrot min="0.000000" max="0.000000" cur="0.000000" />
// To:   <yrot min="INF" max="INF"/>

這甚至可以使用 awk 之類的嗎?還是我需要使用某種特殊的 XML 解析器?

編輯:要清楚,大約有十幾個標籤屬於 ,其中一個是 . 只出現在標籤內。如果名稱包含“wheel”,我只想替換該行。本身是嵌套的。

對於那些聲稱我需要 XML 解析器的人,如果滿足條件(yrot 標籤在輪子中),為什麼不只是簡單的文本查找/替換工作?檢查有那麼難嗎?

使用 python 的 ElementTree 標準庫:

#! /usr/bin/env python

import sys
import xml.etree.ElementTree as ET

def do_one(file_name):
   tree = ET.parse(file_name)

   for part in tree.findall("part"):
       if not 'wheel' in part.attrib['name']:
           continue
       for yrot in part.findall('yrot'):
           names = []
           for x in yrot.attrib:
               names.append(x)
           for x in names:
               del yrot.attrib[x]
           yrot.attrib['min'] = 'INF'
           yrot.attrib['max'] = 'INF'

   tree.write(file_name)

for file_name in sys.argv[1:]:
   do_one(file_name)

這會將命令行上傳遞的所有文件解析到腳本:

python convert_xml.py *.xml

將您的 XML 提供data.xml為:

$ cat data.xml
   <?xml version="1.0" encoding="UTF-8"?>
   <root>
      <part name="D_wheel1" seqNumber="1">
         <yrot min="0.000000" max="0.000000" cur="0.000000" />
      </part>
      <part name="D_wheel2" seqNumber="1">
         <yrot min="0.000000" max="0.000000" cur="0.000000" />
      </part>
      <part name="door" seqNumber="1">
         <yrot min="0.000000" max="0.000000" cur="0.000000" />
      </part>
   </root>

xmlstarletXPath一起使用:

$ xmlstarlet ed \
   --var target '//part[contains(@name, "wheel")]/yrot' \
   -u '$target/@*[name()="min" or name()="max"]' -v 'INF' \
   -d '$target/@cur' data.xml

<?xml version="1.0" encoding="UTF-8"?>
<root>
 <part name="D_wheel1" seqNumber="1">
   <yrot min="INF" max="INF"/>
 </part>
 <part name="D_wheel2" seqNumber="1">
   <yrot min="INF" max="INF"/>
 </part>
 <part name="door" seqNumber="1">
   <yrot min="0.000000" max="0.000000" cur="0.000000"/>
 </part>
</root>

或使用XSLT的經典方法:和xsltprocxmlstarlet

$ cat data.xsl 
<?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" version="1.0" encoding="UTF-8" indent="yes"/>
   <xsl:template match="@*|node()">
       <xsl:copy>
           <xsl:apply-templates select="@*|node()"/>
       </xsl:copy>
   </xsl:template>
   <xsl:template match="*[contains(@name, 'wheel')]/yrot">
       <xsl:copy>
           <xsl:attribute name="min">INF</xsl:attribute>
           <xsl:attribute name="max">INF</xsl:attribute>
       </xsl:copy>
   </xsl:template>
</xsl:stylesheet>

$ xsltproc data.xsl data.xml #or: xmlstarlet tr data.xsl data.xml
<?xml version="1.0" encoding="UTF-8"?>
<root>
  <part name="D_wheel1" seqNumber="1">
     <yrot min="INF" max="INF"/>
  </part>
  <part name="D_wheel2" seqNumber="1">
     <yrot min="INF" max="INF"/>
  </part>
  <part name="door" seqNumber="1">
     <yrot min="0.000000" max="0.000000" cur="0.000000"/>
  </part>
</root>

引用自:https://unix.stackexchange.com/questions/209862