Altera o texto na tag, mas apenas a tag está contida em um determinado bloco XML

2

Usando o Git Bash, estou tentando substituir condicionalmente o que está na tag do yrot em centenas de arquivos, mas somente se ele pertence a uma tag de nome de parte que pertence à roda.

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

Isso é possível usando os gostos do awk? Ou preciso usar algum tipo de analisador XML especial?

EDIT: Para ser claro, existem cerca de uma dúzia de tags que pertencem a um deles sendo um. aparece apenas dentro de uma tag. Eu só quero substituir a linha se o nome contiver "roda". está aninhado.

Para aqueles que reivindicam que eu preciso de um analisador XML, por que apenas um texto simples não encontraria / substituiria o trabalho se a condição fosse atendida (a tag yrot está em rodas)? Está verificando isso tão difícil?

    
por Stradigos 15.06.2015 / 20:49

4 respostas

2

Usando a biblioteca padrão ElementTree do python:

#! /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)

Isso analisa todo o arquivo entregue na linha de comando para o script:

python convert_xml.py *.xml
    
por 15.06.2015 / 21:22
3

Forneceu seu XML em data.xml como:

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

Usando xmlstarlet com 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>

Ou a abordagem clássica usando XSLT : e xsltproc ou 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>
    
por 15.06.2015 / 22:12
2

Existe um grande problema em tentar analisar XML com ferramentas unix 'padrão'. XML é uma estrutura de dados e suporta uma variedade de layouts que são semanticamente idênticos, mas não possuem a mesma linha e indentação.

Isso significa que é realmente uma má ideia analisar como base de linha / regex, porque você estará criando um código fundamentalmente frágil. Alguém pode reestruturar seu XML em algum momento, e seu código será quebrado sem nenhuma razão óbvia. Esse é o tipo de coisa que dá aos programadores de manutenção e futuros administradores uma dor real.

Então, sim, por favor use um analisador XML. Há uma variedade de opções - alguém te deu uma opção de python, então estou incluindo o perl aqui também.

#!/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;

Agora, quanto à razão 'verificar', o seu texto é difícil - tudo isso é o mesmo:

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

E:

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

E:

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

Eles são todos semanticamente idênticos, mas espero que você possa ver - não irá analisar o mesmo. Coisas como tags unárias - como >

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

Vs:

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

Também - semanticamente idêntico. Então você pode sair com a linha e o regex, mas está apostando e construindo códigos frágeis.

    
por 15.06.2015 / 22:06
0

Usando o awk. Note que isso pressupõe uma estrutura de arquivos muito simples como a que você mostra. Não posso garantir que funcionará em arquivos XLM arbitrários. Na verdade, posso garantir que não.

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

Falando sério, isso é tão frágil quanto possível. Ele assume que o name= é sempre o segundo campo delimitado por espaço na linha, ele quebra em tags aninhadas e todos os tipos de outras possíveis complicações. Ele fornece a saída desejada no exemplo que você forneceu, mas quebrará a menor mudança que você fizer nos arquivos. A abordagem da Anthon usando um parser adequado é muito mais segura.

    
por 15.06.2015 / 21:46