Manipulação de linha de comando XML (script de shell)

9

Como manipular o XML a partir da linha de comando no shell script?

Existem muitos comandos para manipular dados tabulares, substituindo a variável de ambiente ou substituindo fragmentos de texto por regex, mas não encontrei nada para XML.

Meu script de construção precisa inserir uma tag com conteúdo dentro da tag principal do documento xml, e acho um exagero instalar o java, perl ou python no SO para essa finalidade (meus scripts são feitos no gitlab com imagens docker , fazendo meu trabalho com ferramentas disponíveis no maven: a imagem 3.5-jdk-8 seria um sonho).

Eu não quero manipular XML com sed, embora no meu script de compilação funcione, porque é mal .

Exemplo: eu tenho o seguinte xml:

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>  
  <!-- a lot of other tags-->
</project>  

E eu quero inserir o seguinte bloco:

<distributionManagement>
    <repository>
        <id>private-releases</id>
        <url>https://my.private.server.com/nexus/repository/maven-releases/</url>
    </repository>
</distributionManagement>

dentro da tag do projeto (e não importa se será no começo ou no fim.

    
por 9ilsdx 9rvj 0lo 09.02.2018 / 13:08

2 respostas

10

O XMLStarlet ( link ) é escrito em C e usa libxml2 e libxslt .

Dado o documento XML

<?xml version="1.0"?>
<root>
  <tag>data</tag>
</root>

um subnó para root pode ser inserido usando

xml ed -s '/root' -t elem -n 'newtag' -v 'newdata' file.xml

que produz

<?xml version="1.0"?>
<root>
  <tag>data</tag>
  <newtag>newdata</newtag>
</root>

Inserindo muitas coisas (usando o original file.xml no topo aqui):

xml ed -s '/root' -t elem -n 'newtag' \
       -s '/root/newtag' -t elem -n 'subtag' -v 'subdata' file.xml

Isso produz

<?xml version="1.0"?>
<root>
  <tag>data</tag>
  <newtag>
    <subtag>subdata</subtag>
  </newtag>
</root>

Para o exemplo na pergunta:

xml ed -N x="http://maven.apache.org/POM/4.0.0" \
       -s '/x:project' -t elem -n 'distributionManagement' \
       -s '/x:project/distributionManagement' -t elem -n 'repository' \
       -s '/x:project/distributionManagement/repository' -t elem -n 'id' \
         -v 'private-releases' \
       -s '/x:project/distributionManagement/repository' -t elem -n 'url' \
         -v 'https://my.private.server.com/nexus/repository/maven-releases/' \
    file.xml

Resultado:

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- a lot of other tags-->
  <distributionManagement>
    <repository>
      <id>private-releases</id>
      <url>https://my.private.server.com/nexus/repository/maven-releases/</url>
    </repository>
  </distributionManagement>
</project>

Inserindo um arquivo XML preparado anteriormente em um local no XML:

Supondo que o XML original da questão está em file.xml e os bits adicionais que devem ir no novo nó distributinManagement estão em new.xml (mas não a própria tag do nó), um poderia fazer o seguinte para inserir new.xml no nó raiz:

xml ed -N x="http://maven.apache.org/POM/4.0.0" \
       -s '/x:project' -t elem -n 'distributionManagement' \
       -v "$(<new.xml)" file.xml | xml unesc | xml fo

O XMLStarlet escapará automaticamente dos dados que precisam ser salvos, como < e > caracteres. O xml unesc bit remove os dados inseridos (na verdade, ele descarta o documento inteiro, o que pode ou não ser um problema) e xml fo reformata o documento XML resultante.

O resultado é

<?xml version="1.0"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
  </properties>
  <!-- a lot of other tags-->
  <distributionManagement>
    <repository>
      <id>private-releases</id>
      <url>https://my.private.server.com/nexus/repository/maven-releases/</url>
    </repository>
  </distributionManagement>
</project>

Estou um pouco desconfortável em fazer isso dessa maneira, "mas funciona".

Veja também esta questão relacionada no StackOverflow: link

    
por 09.02.2018 / 13:21
1

I find it an overkill to install java, perl or python in OS for that purpose (my scripts are done in gitlab with docker images, so doing my job with tools available in maven:3.5-jdk-8 image would be a dream).

provavelmente ainda é um exagero, mas se você estiver preocupado apenas com o tamanho do contêiner, você pode usar uma linguagem muito leve, como Lua ou Guile.

da documentação do Lua:

Adding Lua to an application does not bloat it. The tarball for Lua 5.3.4, which contains source code and documentation, takes 297K compressed and 1.1M uncompressed. The source contains around 24000 lines of C. Under 64-bit Linux, the Lua interpreter built with all standard Lua libraries takes 246K and the Lua library takes 421K.

    
por 09.02.2018 / 18:54