Git Bashを使用して、何百ものファイルのyrotタグの内容を条件付きで置き換えようとしています。ただし、その内容がホイール関連部品名タグに属する場合にのみ可能です。
// 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パーサーを使用する必要がありますか?
編集:明確に言えば、に属するタグは約12個あり、そのうちの1つはタグ内にのみ表示されます。名前に「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=
フィールドを区切る行の2番目の空白は、常に入れ子になったタグや他のさまざまな合併症がある場合は中断されると想定されます。提供した例では、目的の出力を提供していますが、ファイルを少し変更するだけで中断されます。適切なパーサーを使用するAnthonのアプローチははるかに安全です。