web-dev-qa-db-fra.com

Extraction de données à partir d'un simple fichier XML

J'ai un fichier XML avec le contenu:

<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>

J'ai besoin d'un moyen d'extraire ce qui est dans les balises <job..></job>, programmin dans ce cas. Cela devrait être fait à l'invite de la commande linux, en utilisant grep/sed/awk.

37
Zacky112

Est-ce que vous devez vraiment utiliser uniquement ces outils? Ils ne sont pas conçus pour le traitement XML, et bien qu'il soit possible d'obtenir quelque chose qui fonctionne correctement la plupart du temps, cela échouera dans les cas Edge, comme l'encodage, les sauts de ligne, etc.

Je recommande xml_grep:

xml_grep 'job' jobs.xml --text_only

Ce qui donne la sortie:

programming

Sous Ubuntu/Debian, xml_grep est dans le paquetage xml-twig-tools.

61
amarillion
 grep '<job' file_name | cut -f2 -d">"|cut -f1 -d"<"
12
Vijay

Utiliser xmlstarlet:

echo '<job xmlns="http://www.sample.com/">programming</job>' | \
   xmlstarlet sel -N var="http://www.sample.com/" -t -m "//var:job" -v '.'
9
lmxy

N'utilisez pas d'analyse XML basée sur les lignes et les expressions régulières. C'est une mauvaise idée. Vous pouvez avoir un code XML sémantiquement identique avec un formatage différent, et l'analyse syntaxique basée sur les expressions rationnelles et les lignes ne peut tout simplement pas y faire face. 

Des choses comme les étiquettes unaires et le retour à la ligne variable - ces extraits «disent» la même chose:

<root>
  <sometag val1="fish" val2="carrot" val3="narf"></sometag>
</root>


<root>
  <sometag
      val1="fish"
      val2="carrot"
      val3="narf"></sometag>
</root>

<root
><sometag
val1="fish"
val2="carrot"
val3="narf"
></sometag></root>

<root><sometag val1="fish" val2="carrot" val3="narf"/></root>

Espérons que cela montre clairement pourquoi il est difficile de créer un analyseur basé sur les expressions rationnelles/lignes. Heureusement, vous n'en avez pas besoin. De nombreux langages de script ont au moins une, parfois plus d'options d'analyse. 

Comme l'a déjà mentionné une affiche, xml_grep est disponible. C'est en fait un outil basé sur la bibliothèque XML::Twig Perl. Cependant, il utilise des "expressions xpath" pour trouver quelque chose et différencie la structure du document, les attributs et le "contenu". 

Par exemple.:

xml_grep 'job' jobs.xml --text_only

Cependant, dans l’intérêt de fournir de meilleures réponses, voici quelques exemples de "lancez votre propre" en fonction de vos données source:

Première manière:

Utilisez twig handlers qui capture les éléments d’un type particulier et les applique. L'avantage de le faire de cette façon est qu'il analyse le XML "au fur et à mesure" et vous permet de le modifier en vol si vous en avez besoin. Ceci est particulièrement utile pour rejeter du XML "traité" lorsque vous travaillez avec des fichiers volumineux, en utilisant purge ou flush:

#!/usr/bin/Perl

use strict;
use warnings;

use XML::Twig;

XML::Twig->new(
    twig_handlers => {
        'job' => sub { print $_ ->text }
    }
    )->parse( <> );

Qui utilisera <> pour prendre une entrée (intégrée ou spécifiée via la ligne de commande ./myscript somefile.xml) et la traiter - chaque élément job sera extrait et imprimera le texte associé. (Vous voudrez peut-être que print $_ -> text,"\n" insère un saut de ligne). 

Parce qu'il correspond aux éléments 'job', il va également correspondre aux éléments de travail imbriqués:

<job>programming
    <job>anotherjob</job>
</job>

Correspondra deux fois, mais imprimera aussi une partie de la sortie deux fois. Vous pouvez toutefois faire correspondre le /job à la place si vous préférez. Utilement - cela vous permet par exemple imprimez et supprimez un élément ou copiez-collez-en un en modifiant la structure XML. 

Vous pouvez également analyser en premier et imprimer en fonction de la structure:

my $twig = XML::Twig->new( )->parse( <> );
print $twig -> root -> text;

Comme job est votre élément racine, il suffit d’en imprimer le texte. 

Mais nous pouvons être un peu plus perspicaces et rechercher job ou /job et l’imprimer spécifiquement à la place:

my $twig = XML::Twig->new( )->parse( <> );
print $twig -> findnodes('/job',0)->text;

Vous pouvez également utiliser l'option XML::Twigs pretty_print pour reformater votre XML:

XML::Twig->new( 'pretty_print' => 'indented_a' )->parse( <> ) -> print;

Il existe une variété d'options de format de sortie, mais pour un XML plus simple (comme le vôtre), la plupart auront un aspect similaire. 

9
Sobrique

il suffit d'utiliser awk, pas besoin d'autres outils externes. Ci-dessous fonctionne si vos balises désirées apparaissent dans multitine.

$ cat file
test
<job xmlns="http://www.sample.com/">programming</job>
<job xmlns="http://www.sample.com/">
programming</job>

$ awk -vRS="</job>" '{gsub(/.*<job.*>/,"");print}' file
programming

programming
8
ghostdog74

En supposant la même ligne, entrée de stdin:

sed -ne '/<\/job>/ { s/<[^>]*>\(.*\)<\/job>/\1/; p }'

notes: -n cesse de tout afficher automatiquement; -e signifie que c'est un one-liner (avec un script) /<\/job> se comporte comme un grep; s supprime les attributs opentag + et l'étiquette finale; ; est une nouvelle déclaration; p impressions; {} permet au grep de s’appliquer aux deux instructions.

5
13ren

Utilisation de sed command:

Exemple:

$ cat file.xml
<note>
        <to>Tove</to>
                <from>Jani</from>
                <heading>Reminder</heading>
        <body>Don't forget me this weekend!</body>
</note>

$ cat file.xml | sed -ne '/<heading>/s#\s*<[^>]*>\s*##gp'
Reminder

Explication:

cat file.xml | sed -ne '/<pattern_to_find>/s#\s*<[^>]*>\s*##gp'

n - supprime l'impression de toutes les lignes
e - script

/<pattern_to_find>/ - recherche les lignes contenant le motif spécifié, ce qui pourrait être par exemple .<heading>

next est la partie de substitution s///p qui supprime tout sauf la valeur souhaitée, où / est remplacé par # pour une meilleure lisibilité:

s#\s*<[^>]*>\s*##gp
\s* - inclut des espaces si existants (idem à la fin)
<[^>]*> représente <xml_tag> en tant que cause alternative regex non gloutonne <.*?> ne fonctionne pas pour sed
g - remplace tout, par ex. fermeture de la balise xml </xml_tag>

3
vldbnc

Un peu tard pour le spectacle.

xmlcutty découpe les nœuds à partir de XML:

$ cat file.xml
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
<job xmlns="http://www.sample.com/">designing</job>
<job xmlns="http://www.sample.com/">managing</job>
<job xmlns="http://www.sample.com/">teaching</job>

L'argument path nomme le chemin d'accès à l'élément que vous voulez couper. Dans ce cas, comme les tags ne nous intéressent pas du tout, nous renommons le tag en \n, nous obtenons donc une liste de Nice:

$ xmlcutty -path /job -rename '\n' file.xml
programming
designing
managing
teaching

Notez que le XML n'était pas valide pour commencer (pas d'élément racine). xmlcutty peut aussi fonctionner avec du XML légèrement cassé.

0
miku

Que diriez-vous:

cat a.xml | grep '<job' | cut -d '>' -f 2 | cut -d '<' -f 1
0
codaddict