Como grep um bloco xml em um arquivo xml usando uma palavra-chave no ksh

0

Eu tenho um arquivo Sample.xml que contém muitos serviços dentro dele e a estrutura se parece com isso

Notas:

  1. Não consigo usar nenhuma ferramenta parser XML, pois não tenho permissão, somente leitura

  2. Minha versão do xmllint não suporta o xpath, e não consigo atualizá-lo, somente leitura

  3. Eu não tenho xmlstarlet e não consigo instalá-lo

PROBLEMA: INPUT: NOME DA FILA

SAÍDA: BLOCO DE SERVIÇO

amostra INPUT: ABC.getme2

OUTPUT NEEDED:

<service name="GETME2" min="1" max="10" idleTime="300" backend="ABC">
                            <handlerContainer className="com.abc.xyz.wqere.abcqwere">
                            <handler className="com.abc.xyz.qweqweqwe.werwerwerwer"/>
                            </handlerContainer>
                            <mqListener queue="ABC.getme2" suggExpiry="30" minExpiry="4" maxExpiry="500" copyMessageId="true"/>
                    </service>

Estrutura XML:

     <?xml version="1.0" encoding="UTF-8"?>
        <deploymentconfig xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
                <configfile>sample.xml</configfile>
                <exceptionsFilterConfigFile>asdasd.xml</exceptionsFilterConfigFile>
                <keyInfoConfigFile>asdasd.xml</keyInfoConfigFile>
                <services>

    <service name="GETME" min="1" max="10" idleTime="300" backend="ABC">
                            <handlerContainer className="com.abc.xyz.wqere.abcqwere">
                            <handler className="com.abc.xyz.qweqweqwe.werwerwerwer"/>
                            </handlerContainer>
                            <mqListener queue="ABC.getme" suggExpiry="30" minExpiry="4" maxExpiry="500" copyMessageId="true"/>
                    </service>

    <service name="GETME2" min="1" max="10" idleTime="300" backend="ABC">
                            <handlerContainer className="com.abc.xyz.wqere.abcqwere">
                            <handler className="com.abc.xyz.qweqweqwe.werwerwerwer"/>
                            </handlerContainer>
                            <mqListener queue="ABC.getme2" suggExpiry="30" minExpiry="4" maxExpiry="500" copyMessageId="true"/>
                    </service>
        . . . .a lot of services like this . . . .
        . . . .a lot of services like this . . . .
        . . . .a lot of services like this . . . .
        . . . .a lot of services like this . . . .
        </services>
   <batchServices>
                        <batchService name="batch1">
                                <executor className="com.abc.xyz.qwer.qweqwewqe.ffdsdfsdfsdfsdf" />
                        </batchService>
                        <batchService name="batch2">
                                <executor className="com.abc.xyz.qwer.qweqwewqe.zxcsadsad" />
                        </batchService>
. . . .a lot of batch services like this . . . .
        . . . .a lot of batch services like this . . . .
        . . . .a lot of batch services like this . . . .
        . . . .a lot of batch services like this . . . .
      </batchServices>

<timerservices>
<timerservice> - a lot of timeservice
</timerservices>

  <connectionPools>
                <pool>
                        <name>asdasd</name>
                        <driver>oracle.jdbc.driver.OracleDriver</driver>
                        <url>$asdasd_URL</url>
                        <userId>$asdasd_USER</userId>
                        <password>$asdasd_PASSWORD</password>
                        <minConnections>0</minConnections>
                        <maxConnections>10</maxConnections>
                        <poolUrl>jdbc:asdsad:asdasdsad</poolUrl>
                        <testSql>select * from abc</testSql>
                </pool>

 . . a lot of pools. . .

</connectionpools>

</deploymentconfig>

Eu preciso de um bloco xml como este:

 <service name="GETME" min="1" max="10" idleTime="300" backend="ABC">
                        <handlerContainer className="com.abc.xyz.wqere.abcqwere">
                        <handler className="com.abc.xyz.qweqweqwe.werwerwerwer"/>
                        </handlerContainer>
                        <mqListener queue="ABC.getme" suggExpiry="30" minExpiry="4" maxExpiry="500" copyMessageId="true"/>
                </service>

e eu só preciso fornecer o nome da fila

QUEUENAME=INSERT_HERE
grep ______________ $QUEUENAME. . . 

Aqui está a saída:

Usage : xmllint [options] XMLfiles ...
    Parse the XML files and output the result of the parsing
    --version : display the version of the XML library used
    --debug : dump a debug tree of the in-memory document
    --shell : run a navigating shell
    --debugent : debug the entities defined in the document
    --copy : used to test the internal copy implementation
    --recover : output what was parsable on broken XML documents
    --noent : substitute entity references by their value
    --noout : don't output the result tree
    --path 'paths': provide a set of paths for resources
    --load-trace : print trace of all external entites loaded
    --nonet : refuse to fetch DTDs or entities over network
    --nocompact : do not generate compact text nodes
    --htmlout : output results as HTML
    --nowrap : do not put HTML doc wrapper
    --valid : validate the document in addition to std well-formed check
    --postvalid : do a posteriori validation, i.e after parsing
    --dtdvalid URL : do a posteriori validation against a given DTD
    --dtdvalidfpi FPI : same but name the DTD with a Public Identifier
    --timing : print some timings
    --output file or -o file: save to a given file
    --repeat : repeat 100 times, for timing or profiling
    --insert : ad-hoc test for valid insertions
    --compress : turn on gzip compression of output
    --html : use the HTML parser
    --xmlout : force to use the XML serializer when using --html
    --push : use the push mode of the parser
    --memory : parse from memory
    --maxmem nbbytes : limits memory allocation to nbbytes bytes
    --nowarning : do not emit warnings from parser/validator
    --noblanks : drop (ignorable?) blanks spaces
    --nocdata : replace cdata section with text nodes
    --format : reformat/reindent the input
    --encode encoding : output in the given encoding
    --dropdtd : remove the DOCTYPE of the input docs
    --c14n : save in W3C canonical format (with comments)
    --exc-c14n : save in W3C exclusive canonical format (with comments)
    --nsclean : remove redundant namespace declarations
    --testIO : test user I/O support
    --catalogs : use SGML catalogs from $SGML_CATALOG_FILES
                 otherwise XML Catalogs starting from 
             file:///etc/xml/catalog are activated by default
    --nocatalogs: deactivate all catalogs
    --auto : generate a small doc on the fly
    --xinclude : do XInclude processing
    --noxincludenode : same but do not generate XInclude nodes
    --loaddtd : fetch external DTD
    --dtdattr : loaddtd + populate the tree with inherited attributes 
    --stream : use the streaming interface to process very large files
    --walker : create a reader and walk though the resulting doc
    --pattern pattern_value : test the pattern support
    --chkregister : verify the node registration code
    --relaxng schema : do RelaxNG validation against the schema
    --schema schema : do validation against the WXS schema
    --schematron schema : do validation against a schematron
    --sax1: use the old SAX1 interfaces for processing
    --sax: do not build a tree but work just at the SAX level

Libxml project home page: http://xmlsoft.org/
To report bugs or get some help check: http://xmlsoft.org/bugs.html

Aqui está a versão

xmllint: using libxml version 20626
    
por Philip Morris 01.06.2015 / 22:18

5 respostas

1

Com as condições:

  1. Não consigo usar nenhuma ferramenta parser XML, pois não tenho permissão, somente leitura

  2. Minha versão do xmllint não suporta o xpath, e não consigo atualizá-lo, somente leitura

  3. Eu não tenho xmlstarlet e não consigo instalá-lo

Eu recorri a encontrar outras soluções não convencionais. Este comando awk me deu o que eu precisava

awk '
  /<service.*name=/ { f=1 ; m=0 ; res="" }
  f { res = res $0 ORS }
  f && /mqListener queue="ABC.getme2"/ { m=1 }
  /<\/service>/ { f=0 ; if (m) print res $0 }
' Sample.xml

Agradecimentos especiais a @Janis por me ajudar aqui - Como implementar o padrão de alcance do awk ao buscar um bloco xml quando o parâmetro de entrada está no meio do bloco

    
por 03.06.2015 / 11:31
2

Se você estiver usando o mais recente ksh - com o que quero dizer uma compilação recente de ksh93 - você pode realmente usá-lo. ksh93 suporta tipos de variáveis composto - que são um pouco como uma estrutura C - ou uma árvore de nós XML. Ele não suporta nativamente XML no momento - embora eu acredite que seja planejado - mas suporta json agora.

Eu usei algumas coisas conversor on-line gratuito para obter um json saída de sua amostra. Ainda assim, depois de limpar sua amostra, um pouco (o p em deve ser revestido por cima) Eu poderia fazer:

print -j queue.services.[@name]

... e foi recompensado com ...

GETME

Eu também posso fazer:

print -j queue.services[1].[@name]

... para obter em seu lugar ...

GETME2

No site de conversão vinculada eu tive que selecionar Delimitado por tabulações para evitar que ele ficasse em muitos espaços sem quebra, mas diferente disso parece ter saído ok. Certamente, existem ferramentas que você pode usar com facilidade para fazer conversões semelhantes localmente.

De qualquer forma, com ksh você pode ler em uma árvore json depois de copiá-la para sua área de transferência, assim como eu fiz, como:

read -m json queue <<<"$(xsel -bo)"

Depois de fazer isso, eu pude ver toda a estrutura como ...

print -j queue

... que imprimiu ...

{
    "batchServices": [
        {
            "@name": "batch1",
            "executor": {
                "@className": "com.abc.xyz.qwer.qweqwewqe.ffdsdfsdfsdfsdf"
            }
        },
        {
            "@name": "batch2",
            "executor": {
                "@className": "com.abc.xyz.qwer.qweqwewqe.zxcsadsad"
            }
        }
    ],
    "configfile": "sample.xml",
    "connectionPools": [
        {
            "driver": "oracle.jdbc.driver.OracleDriver",
            "maxConnections": "10",
            "minConnections": "0",
            "name": "asdasd",
            "password": "$asdasd_PASSWORD",
            "poolUrl": "jdbc:asdsad:asdasdsad",
            "testSql": "select * from abc",
            "url": "$asdasd_URL",
            "userId": "$asdasd_USER"
        }
    ],
    "exceptionsFilterConfigFile": "asdasd.xml",
    "keyInfoConfigFile": "asdasd.xml",
    "services": [
        {
            "@backend": "ABC",
            "@idleTime": "300",
            "@max": "10",
            "@min": "1",
            "@name": "GETME",
            "handlerContainer": {
                "@className": "com.abc.xyz.wqere.abcqwere",
                "handler": {
                    "@className": "com.abc.xyz.qweqweqwe.werwerwerwer"
                }
            },
            "mqListener": {
                "@copyMessageId": "true",
                "@maxExpiry": "500",
                "@minExpiry": "4",
                "@queue": "ABC.getme",
                "@suggExpiry": "30"
            }
        },
        {
            "@backend": "ABC",
            "@idleTime": "300",
            "@max": "10",
            "@min": "1",
            "@name": "GETME2",
            "handlerContainer": {
                "@className": "com.abc.xyz.wqere.abcqwere",
                "handler": {
                    "@className": "com.abc.xyz.qweqweqwe.werwerwerwer"
                }
            },
            "mqListener": {
                "@copyMessageId": "true",
                "@maxExpiry": "500",
                "@minExpiry": "4",
                "@queue": "ABC.getme2",
                "@suggExpiry": "30"
            }
        }
    ]
}
    
por 02.06.2015 / 19:58
2

Como mencionado no comentário acima, xmllint pode ser usado como

xmllint --xpath '//service/[@name="GETME"]' Sample.xml

A opção está disponível pelo menos na versão 20903 da libxml.

Um primer na sintaxe do xpath pode ser encontrado aqui: link ou mais autoritativo link

    
por 01.06.2015 / 22:57
0

Supondo um arquivo xml de exemplo como este:

<services>

 <service name="GETME" min="1" max="10" idleTime="300" backend="ABC">
                        <handlerContainer className="com.abc.xyz.wqere.abcqwere">
                        <handler className="com.abc.xyz.qweqweqwe.werwerwerwer"/>
                        </handlerContainer>
                        <mqListener queue="ABC.sadasdasd" suggExpiry="30" minExpiry="4" maxExpiry="500" copyMessageId="true"/>
  </service>

  <service name="GETHIM" min="1" max="10" idleTime="300" backend="ABC">
                        <handlerContainer className="com.abc.xyz.wqere.abcqwere">
                        <handler className="com.abc.xyz.qweqweqwe.werwerwerwer"/>
                        </handlerContainer>
                        <mqListener queue="ABC.sadasdasd" suggExpiry="30" minExpiry="4" maxExpiry="500" copyMessageId="true"/>
  </service>

</services>

Solução usando xmlstarlet

A solução para extrair o valor queue do serviço com name como GETME é a seguinte:

xmlstarlet sel -t -v "/services/service[@name='GETME']/mqListener/@queue" test.xml 

Aqui,

-t  ==>  select template 
-v  ==>  template selected is "value", i.e. value-of <xpath>

Solução usando xmllint

xmllint --xpath "/services/service[@name='GETME']/mqListener/@queue" test.xml 
    
por 02.06.2015 / 16:13
0

OK, em primeiro lugar - não use grep . XML não é um formato adequado para análise baseada em regex. Use um analisador XML em vez disso.

Meu XML Parser favorito é na verdade um módulo perl chamado XML::Twig .

#!/usr/bin/perl

use strict;
use warnings;

use XML::Twig;

my ($keyword, $filename) = @ARGV;

XML::Twig->new(
    'pretty_print'  => 'indented_a',
    'twig_handlers' => {
        'service[@name="'.$keyword.'"]' => sub { $_->print }
    }
)->parsefile($filename);

Invocada com myscript.pl GETME yourxml , será impressa qualquer correspondência. (Altere pretty_print para um formato que você goste).

XML::Twig na verdade vem com alguns exemplos de casos de uso, como xml_grep também, o que provavelmente faz muito do que você deseja de qualquer maneira.

Com o seu exemplo XML acima (ligeiramente hackeado, porque não é válido, e assumi que o seu XML de origem é, e este é um erro de transposição).

<service
        backend="ABC"
        idleTime="300"
        max="10"
        min="1"
        name="GETME">
      <handlerContainer className="com.abc.xyz.wqere.abcqwere">
        <handler className="com.abc.xyz.qweqweqwe.werwerwerwer" />
      </handlerContainer>
      <mqListener
          copyMessageId="true"
          maxExpiry="500"
          minExpiry="4"
          queue="ABC.getme"
          suggExpiry="30"
      />
    </service>

NB: este formato é indented_a in XML::Twig . Outros estão disponíveis. Isso é, pelo menos em parte, uma ilustração do motivo pelo qual a expressão regular e a correspondência baseada em linhas de XML são perigosas.

    
por 02.06.2015 / 16:33