altera uma linha e remove tag usando perl do arquivo xml?

3

Eu tenho um arquivo xml (client_23.xml) no qual eu preciso alterar uma linha e remover uma tag inteira dela, então eu criei o script perl:

No meu arquivo xml, eu tenho um bloco como este. Haverá apenas uma instância de <hello>collect_model = 1</hello> no meu arquivo xml:

<world>
    <hello>collect_model = 1</hello>
    <hello>enable_data = 0</hello>
    <hello>session_ms = 2*60*1000</hello>
    <hello>max_collect = string_integer($extract("max_collect"))</hello>
    <hello>max_collect = parenting(max_collect, max_collect, 1.0e99)</hello>
    <hello>output('{')</hello>
</world>

Eu preciso mudar essa linha para isso: <hello>collect_model = 0</hello> , então meu bloco inteiro deve ser assim depois da alteração:

<world>
    <hello>collect_model = 0</hello>
    <hello>enable_data = 0</hello>
    <hello>session_ms = 2*60*1000</hello>
    <hello>max_collect = string_integer($extract("max_collect"))</hello>
    <hello>max_collect = parenting(max_collect, max_collect, 1.0e99)</hello>
    <hello>output('{')</hello>
</world>

Segundo, preciso remover essa tag inteira do mesmo arquivo xml:

<derta-config>
    <data-users>2000</data-users>
    <test-users>2000</test-users>
    <attributes>hello world</attributes>
    <client-types>Client1</model-types>
    <target>price.world</target>
</derta-config>

Então eu tenho abaixo shell script em que estou usando perl que tenta fazer acima de duas coisas, juntamente com a substituição de algum conteúdo em um arquivo (que estou fazendo para algum outro propósito), mas a parte que eu adicionei especificamente para acima de dois não funciona e começa a imprimir muitos erros:

perl -0pe "s#<eval>collect_model = 0</eval>#<eval>collect_model = 1</eval> s#<derta-config>.* </derta-config>##sm;   s#<function>\s*<name>DUMMY_FUNCTION.+?</function>#$file#sm" client_"$client_id".xml > "$word"_new_file.xml

Então, eu estou pensando, podemos fazer isso no shell script, ou seja, remetemos as duas coisas acima usando o shell script e então a saída disso, podemos passar para o meu script perl que está trabalhando na terceira etapa. Então, podemos passar a saída do script de shell que irá remover as duas coisas acima para mim para este script abaixo perl? Isso é possível fazer?

perl -0pe "s#<function>\s*<name>DUMMY_FUNCTION.+?</function>#$file#sm" client_"$client_id".xml > "$word"_dyn_model.xml

Aqui $client_id é 23 e $word é abc .

Estou apenas tentando fazer isso funcionar e qualquer que seja a maneira mais fácil de fazer isso para mim. Eu só terei uma instância de todas as duas coisas acima que mencionei.

    
por user1950349 17.09.2015 / 08:15

2 respostas

2

Com isso como o arquivo de entrada de amostra:

$ cat client_23.xml 
<world>
    <hello>collect_model = 1</hello>
    <hello>enable_data = 0</hello>
    <hello>session_ms = 2*60*1000</hello>
    <hello>max_collect = string_integer($extract("max_collect"))</hello>
    <hello>max_collect = parenting(max_collect, max_collect, 1.0e99)</hello>
    <hello>output('{')</hello>
</world>
<derta-config>
    <data-users>2000</data-users>
    <test-users>2000</test-users>
    <attributes>hello world</attributes>
    <client-types>Client1</model-types>
    <target>price.world</target>
</derta-config>

Podemos fazer as duas alterações usando:

$ sed 's|<hello>collect_model = 1</hello>|<hello>collect_model = 0</hello>|; \|<derta-config>|,\|</derta-config>|d' client_23.xml 
<world>
    <hello>collect_model = 0</hello>
    <hello>enable_data = 0</hello>
    <hello>session_ms = 2*60*1000</hello>
    <hello>max_collect = string_integer($extract("max_collect"))</hello>
    <hello>max_collect = parenting(max_collect, max_collect, 1.0e99)</hello>
    <hello>output('{')</hello>
</world>

Como funciona

Nós temos dois comandos sed. O primeiro é um substituto, o segundo é uma exclusão:

  • s|<hello>collect_model = 1</hello>|<hello>collect_model = 0</hello>|

    Os comandos substitutos têm o formato s|old|new| . Então, aqui old é o original <hello>collect_model = 1</hello> e new é o substituto <hello>collect_model = 0</hello> .

  • \|<derta-config>|,\|</derta-config>|d

    Isso define um intervalo de linhas. A linha de partida contém derta-config> e a linha final contém </derta-config> . Todas as linhas dentro desse intervalo são excluídas pelo comando de exclusão d .

por 17.09.2015 / 08:28
3

Por favor, não use expressões regulares para analisar XML. É uma má idéia. A principal razão pela qual é uma má ideia é porque há muita variedade em XML - e alguns XML semanticamente idênticos podem ter uma correspondência de padrões significativamente diferente.

Considere feeds de linha, espaço em branco, tags unários, etc.

<element />
<element></element>

São os mesmos - e então você pode recuar, alinhar, dividir tags, etc:

<element 
    att1="fish"
    att2="carrot">

também é válido.

Por isso, sugiro strongmente sugerir 'usar um analisador'. Perl tem várias opções - eu gosto de XML::Twig :

#!/usr/bin/env perl
use strict;
use warnings;
use XML::Twig;

my $twig = XML::Twig->new( 'pretty_print' => 'indented_a' )->parse( \*DATA );

foreach my $hello ( $twig->findnodes('//hello') ) {
    if ( $hello->trimmed_text =~ m/collect_model/ ) {
        $hello->set_text('collect_model = 0');
    }
}

$_->delete for $twig->findnodes('//derta-config');

$twig->print;

__DATA__
<root>
<world>
    <hello>collect_model = 1</hello>
    <hello>enable_data = 0</hello>
    <hello>session_ms = 2*60*1000</hello>
    <hello>max_collect = string_integer($extract("max_collect"))</hello>
    <hello>max_collect = parenting(max_collect, max_collect, 1.0e99)</hello>
    <hello>output('{')</hello>
</world>
<derta-config>
    <data-users>2000</data-users>
    <test-users>2000</test-users>
    <attributes>hello world</attributes>
    <client-types>Client1</client-types>
    <target>price.world</target>
</derta-config>
</root>

Porque você parece gostar de um liner em perl:

perl -MXML::Twig -0777 -e 'my $twig = XML::Twig->parse (<>); $_->set_text("collect_model = 0") for grep { $_->text =~ m/collect_model/ } $twig->findnodes("//hello"); $_->delete for $twig->findnodes("//derta-config"); $twig -> print;'
    
por 21.09.2015 / 13:31