Измените текст в теге, но только тег содержится в определенном блоке XML

Используя 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?

EDIT: Чтобы быть ясным, существует около десяти тегов, принадлежащих одному из них: a. отображается только в теге. Я хочу только заменить строку, если имя содержит «колесо». сам вложен.

Тем, кто утверждает, что мне нужен синтаксический анализатор 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> 

Использование xmlstarlet с XPath :

 $ 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> 

Существует серьезная проблема с попыткой разобрать XML с помощью стандартных инструментов unix. 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; 

Теперь, по причине «проверки» вашего текста является diffcult – все они одинаковы:

 <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" /> 

Vs:

  <yrot cur="0.000000" max="0.000000" min="0.000000" ></yrot> 

Также – семантически идентичны. Таким образом, вы можете уйти с линейным и регулярным выражением, но он принимает азартные игры и строит хрупкий код.

Использование 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 с использованием собственного анализатора намного безопаснее.