使用 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 标签在轮子中),为什么简单的文本查找/替换不起作用?检查有那么难吗?
答案1
将您的 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>
xmlstarlet
与使用X路径:
$ 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: 和xsltproc
或xmlstarlet
$ 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>
答案2
使用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
答案3
尝试使用“标准”unix 工具解析 XML 存在一个巨大的问题。 XML 是一种数据结构,它支持多种语义相同但不具有相同行和缩进的布局。
这意味着解析基于行/正则表达式确实是一个坏主意,因为您将创建一些根本上脆弱的代码。有人可能会在某个时候重组他们的 XML,而您的代码会无缘无故地崩溃。这种事情会给维护程序员和未来的系统管理员带来一些真正的痛苦。
所以,是的,请使用 XML 解析器。有多种选择 - 有人给了你一个 python 选项,所以我在这里也包括了 perl。
#!/usr/bin/perl
use strict;
use warnings;
use XML::Twig;
sub process_part {
my ( $twig, $part ) = @_;
if ( $part->att('name') =~ m/wheel/ ) {
$part->first_child('yrot')->set_att( 'min', 'INF' );
$part->first_child('yrot')->set_att( 'max', 'INF' );
}
}
my $twig = XML::Twig->new(
'pretty_print' => 'indented_a',
'twig_handlers' => { 'part' => \&process_part }
);
$twig->parsefile('your_file.xml');
$twig->print;
现在,至于“检查”文本很困难的原因 - 这些都是相同的:
<root>
<part
name="D_wheel1"
seqNumber="1">
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
</part>
<part
name="D_wheel2"
seqNumber="1">
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
</part>
<part
name="door"
seqNumber="1">
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
</part>
</root>
和:
<root><part name="D_wheel1" seqNumber="1"><yrot cur="0.000000" max="0.000000" min="0.000000"/></part><part name="D_wheel2" seqNumber="1"><yrot cur="0.000000" max="0.000000" min="0.000000"/></part><part name="door" seqNumber="1"><yrot cur="0.000000" max="0.000000" min="0.000000"/></part></root>
和:
<root
><part
name="D_wheel1"
seqNumber="1"
><yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/></part><part
name="D_wheel2"
seqNumber="1"
><yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/></part><part
name="door"
seqNumber="1"
><yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/></part></root>
它们在语义上都是相同的,但希望如您所见 - 不会解析相同的内容。像一元标签之类的东西 - 比如>
<yrot
cur="0.000000"
max="0.000000"
min="0.000000"
/>
对比:
<yrot cur="0.000000" max="0.000000" min="0.000000" ></yrot>
而且 - 语义相同。那么你能摆脱行和正则表达式的束缚,但这是一场赌博并构建了脆弱的代码。
答案4
使用 awk。请注意,这假设了一个非常简单的文件结构,如您所展示的那样。我不能保证它适用于任意 XLM 文件。事实上,我可以断然保证不会。
awk '{if(/<\/part>/){p=0}if($1~/<part/ && $2~/wheel/){p=1}
if(p==1 && /<yrot/){
print "<yrot min=\"INF\" max=\"INF\"/>"
} else{print}}' file
但说真的,这非常脆弱。它假设name=
始终是行上的第二个空格分隔字段,它会在嵌套标签和各种其他可能的复杂情况下中断。它会在您给出的示例中提供您想要的输出,但它会因您对文件所做的最微小的更改而中断。 Anthon 使用适当解析器的方法要安全得多。