Se você realmente deseja sed
- ou awk
- como processamento de linha de comando para arquivos XML, provavelmente deve considerar o uso de uma ferramenta de linha de comando de processamento XML. Aqui estão algumas das ferramentas que eu mais vi usadas:
Você também deve estar ciente de que existem várias linguagens de programação / consulta específicas para XML:
Observe que (para ser um XML válido) seus dados XML precisam de um nó raiz e que seus valores de atributo devem ser citados, ou seja, seu arquivo de dados deve ficar mais parecido com isto:
<!-- data.xml -->
<instances>
<instance ab='1'>
<a1>aa</a1>
<a2>aa</a2>
</instance>
<instance ab='2'>
<b1>bb</b1>
<b2>bb</b2>
</instance>
<instance ab='3'>
<c1>cc</c1>
<c2>cc</c2>
</instance>
</instances>
Se os seus dados estiverem formatados como XML válido, você poderá usar o XPath com xmlstarlet para obter exatamente o que você quer com um comando muito conciso:
xmlstarlet sel -t -m '//instance' -c "./*" -n data.xml
Isso produz a seguinte saída:
<a1>aa</a1><a2>aa</a2>
<b1>bb</b1><b2>bb</b2>
<c1>cc</c1><c2>cc</c2>
Ou você pode usar o Python (minha escolha favorita). Aqui está um script Python que realiza a mesma tarefa:
#!/usr/bin/env python2
# -*- encoding: ascii -*-
"""extract_instance_children.bash"""
import sys
import xml.etree.ElementTree
# Load the data
tree = xml.etree.ElementTree.parse(sys.argv[1])
root = tree.getroot()
# Extract and output the child elements
for instance in root.iter("instance"):
print(''.join([xml.etree.ElementTree.tostring(child).strip() for child in instance]))
E aqui está como você pode executar o script:
python extract_instance_children.py data.xml
Isso usa o pacote xml da Biblioteca Padrão do Python , que também é um rígido Analisador XML.
Se você não está preocupado em ter XML formatado corretamente e quer apenas analisar um arquivo de texto que parece mais ou menos aquele que você apresentou, então você pode definitivamente realizar o que você quer usando apenas shell-scripting e comando padrão ferramentas de linha. Aqui está um script awk
(conforme solicitado):
#!/usr/bin/env awk
# extract_instance_children.awk
BEGIN {
addchild=0;
children="";
}
{
# Opening tag for "instance" element - set the "addchild" flag
if($0 ~ "^ *<instance[^<>]+>") {
addchild=1;
}
# Closing tag for "instance" element - reset "children" string and "addchild" flag, print children
else if($0 ~ "^ *</instance>" && addchild == 1) {
addchild=0;
printf("%s\n", children);
children="";
}
# Concatenating child elements - strip whitespace
else if (addchild == 1) {
gsub(/^[ \t]+/,"",$0);
gsub(/[ \t]+$/,"",$0);
children=children $0;
}
}
Para executar o script de um arquivo, você usaria um comando como este:
awk -f extract_instance_children.awk data.xml
E aqui está um script Bash que produz a saída desejada:
#!/bin/bash
# extract_instance_children.bash
# Keep track of whether or not we're inside of an "instance" element
instance=0
# Loop through the lines of the file
while read line; do
# Set the instance flag to true if we come across an opening tag
if echo "${line}" | grep -q '<instance.*>'; then
instance=1
# Set the instance flag to false and print a newline if we come across a closing tag
elif echo "${line}" | grep -q '</instance>'; then
instance=0
echo
# If we're inside an instance tag then print the child element
elif [[ ${instance} == 1 ]]; then
printf "${line}"
fi
done < "${1}"
Você executaria assim:
bash extract_instance_children.bash data.xml
Ou, voltando ao Python mais uma vez, você poderia usar o pacote Beautiful Soup . O Beautiful Soup é muito mais flexível em sua capacidade de analisar XML inválido do que o módulo XML Python padrão (e todos os outros analisadores XML que eu encontrei). Aqui está um script Python que usa Beautiful Soup para alcançar o resultado desejado:
#!/usr/bin/env python2
# -*- encoding: ascii -*-
"""extract_instance_children.bash"""
import sys
from bs4 import BeautifulSoup as Soup
with open(sys.argv[1], 'r') as xmlfile:
soup = Soup(xmlfile.read(), "html.parser")
for instance in soup.findAll('instance'):
print(''.join([str(child) for child in instance.findChildren()]))