Substituição de XML com base no conteúdo do atributo usando sed

2

Eu preciso substituir algum conteúdo de atributo em uma tag XML, dependendo do parâmetro $1 .

Entramos na entrada, por exemplo:

<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true">
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="true">

Se o atributo testname não contiver $1 , substitua o valor enabled por false ; caso contrário ( testname faz conter $1 ), substitua o valor enabled por true .

OBSERVAÇÃO: é possível encontrar outros atributos, mais do que neste exemplo.

Eu pensei em sed , mas talvez outras ferramentas possam fazer melhor?

    
por buzz2buzz 11.05.2016 / 13:55

6 respostas

0

Se eu entendi a lógica corretamente, esse comando sed procura o parâmetro $ 1 fornecido dentro do valor testname ; se estiver presente, pesquise e substitua o valor enabled de falso para verdadeiro. Se não estiver ( ! ) presente, substitua o valor enabled de verdadeiro para falso.

sed '/ testname="[^"]*'$1'[^"]*"/ s/ enabled="false"/ enabled="true"/;
     / testname="[^"]*'$1'[^"]*"/!s/ enabled="true"/ enabled="false"/' input > output

Eu tentei ajudar a correspondência de regex dando espaços à esquerda antes dos nomes de atributo (testname e enabled) e usando a classe de caractere [^"] .

    
por 11.05.2016 / 16:29
3

Ninguém disse isso ainda, então eu vou. POR FAVOR, não analise XML usando expressões regulares. XML é uma linguagem contextual e expressões regulares não são. Isso significa que você cria um código frágil, que um dia pode simplesmente quebrar.

Para mais exemplos, consulte: link

POR FAVOR, use um analisador. Eles existem em muitos idiomas - pessoalmente, eu gosto de perl , e sua tarefa é um pouco assim:

#!/usr/bin/env perl
use strict;
use warnings;

#parser library
use XML::Twig; 

#ingest data 
my $twig = XML::Twig -> parse (\*DATA); 

#iterate all tags <ThreadGroup>
foreach my $group  ( $twig -> get_xpath('//ThreadGroup') ) {
   #check testname regex match
   if ( $group -> att('testname') =~ /AA/ ) { 
       #set enabled
       $group -> set_att('enabled', 'true');
   }
   else {
      #set disabled
      $group -> set_att('enabled', 'false'); 
   }
}

#pretty print options vary, see man page. 
$twig -> set_pretty_print('indented_a');
$twig -> print;

__DATA__
<xml>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true" />
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="true" />
</xml>

E sim - é necessário usar um analisador XML, porque as expressões regulares não podem fazê-lo com segurança. Há um monte de coisas em XML que são semanticamente idênticas, como ordenação de atributos, feeds de linha, tags unários, etc., que não são o mesmo regex. Mas um analisador não será pego por isso.

Os itens acima podem ser reduzidos a um liner se você preferir:

perl -MXML::Twig -e 'XML::Twig -> new ( twig_handlers => { ThreadGroup => sub { $_ -> set_att("enabled", $_ -> att("testname") =~ /AA/ ? "true" : "false" ) } } ) -> parsefile_inplace("yourfile")'

Sua equipe de sysadmin deve agradecê-lo por fazer isso (isso não quer dizer que eles o farão) porque qualquer solução baseada em expressões regulares pode quebrar um dia, sem motivo aparente.

Como um exemplo mais trivial - seu XML é semanticamente idêntico como:

<xml>
  <ThreadGroup
      enabled="true"
      guiclass="ThreadGroupGui"
      testclass="ThreadGroup"
      testname="OO CSS DPM PRI"
  />
  <ThreadGroup
      enabled="true"
      guiclass="ThreadGroupGui"
      testclass="ThreadGroup"
      testname="AA CSS DPM PRI"
  />
</xml>

Ou:

<xml>
  <ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="OO CSS DPM PRI"/>
  <ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="AA CSS DPM PRI"/>
</xml>

Ou:

<xml><ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGrou
p" testname="OO CSS DPM PRI"/><ThreadGroup enabled="true" guiclass="ThreadGroupG
ui" testclass="ThreadGroup" testname="AA CSS DPM PRI"/></xml>

Ou:

<xml
><ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="OO CSS DPM PRI"
/><ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="AA CSS DPM PRI"
/></xml>

E isso antes de entrarmos na classificação de atributos, no possível aninhamento de tags ou em outra substring que "corresponde" em lugares que você não espera.

    
por 13.05.2016 / 14:27
0

Dado o nonancient GNU awk (gawk) e assumindo que cada par de testname e enabled esteja na mesma linha separada de qualquer outro par (o que não é um dado para XML em geral):

 awk 'match($0,/ testname="([^"]+)"/,a) {sub(/ enabled="[^"]+"/, " enabled=\"" (a[1]~/AA/?"true":"false") "\"")} 1' <input

Explicação:

match($0,/ testname="([^"]+)"/,a) retorna diferente de zero se a linha de entrada contiver uma substring como testname="abc" AND, pois um efeito colateral coloca a coincidência e a única sub-rotina na matriz a .

{sub(/ enabled="[^"]+"/, " enabled=\"" (a[1]~/AA/?"true":"false") "\"")} é executado se match retornou diferente de zero e procura uma substring como enabled="def" e, se encontrado, substitui-a por uma string no formato enabled="ghi" , em que ghi é true ou false dependendo se a sub-correspondência do match (que é o valor de testname ) coincide com a expressão regular AA , o que para caracteres ordinários o faz se esses caracteres ocorrerem como substring.

Se a string que você está procurando contiver qualquer caractere especial de regexp /.?*+[](){}\ , mas você quiser que eles sejam correspondidos como caracteres reais NOT como um regexp, você deve invertê-los ou (provavelmente mais fácil) usar index(a[1],"AA") , que 'sucede' se AA corresponder como uma subseqüência exata (não regexp) - mas qualquer barra invertida ou doublequote na string literal ainda deve ser invertida.

1 corresponde e (por padrão) imprime cada linha, após a alteração acima, se houver

Se você não tem gawk, mas tem perl , ele pode fazer a mesma coisa com uma sintaxe ligeiramente diferente:

 perl -ne 'if(/ testname="([^"]+)"/){ $x=$1=~/AA/?"true":"false"; s/ enabled="[^"]+"/ enabled="$x"/ };print' input

plus pode substituir o arquivo original por -i , mas eu raramente vejo sistemas com perl mas não gawk.

PS: seu XML postado consiste em tags de abertura, então, se repetido, ele será aninhado indefinidamente, provavelmente não será o que você deseja, e se não houver tags de fechamento não mostradas, ele nem será bem formatado.

    
por 12.05.2016 / 05:38
0

Usando o XMLStarlet :

#!/bin/sh

xml ed -u "//ThreadGroup[. = contains(@testname, '$1')]/@enabled" -v "true"
       -u "//ThreadGroup[. = not(contains(@testname, '$1'))]/@enabled" -v "false"

Supondo que seu XML é válido (adicionei uma tag raiz <SomeTag> e delimitei adequadamente o nó <ThreadGroup> vazio com /> . Também defini os atributos enabled como "hello" para que o script realmente < em> faz algo):

$ cat data.xml
<SomeTag>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="hello"/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="hello"/>
</SomeTag>

$ sh script.sh "OO" <data.xml
<?xml version="1.0"?>
<SomeTag>
  <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true"/>
  <ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="false"/>
</SomeTag>
    
por 11.07.2016 / 14:53
0
#/bin/sh

sed -r 'h
    s/.*testname="([^"]*)".*//
    /\b'"$1"'\b/{ g; s/enabled="[^"]*"/enabled="true"/; b}
    g
    s/enabled="[^"]*"/enabled="false"/
' file
    
por 24.01.2017 / 22:30
-1

Desculpe, tente isto:

cat xml | sed 's/\(testname\=\".*AA.*\"\s\)enabled="\(true\|false\)"/enabled=\"true\"/gi'
    
por 11.05.2016 / 14:17

Tags