2010-02-08 7 views
31

J'ai un fichier XML avec le contenu:Extraction des données à partir d'un simple fichier XML

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

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

+0

Si votre fichier XML contient ceci: < xmlns = emploi "http://www.sample.com/" > Tom & Jerry voudriez-vous le résultat d'avoir XML échappements laissé seul: Tom & Jerry ou si vous voulez que la fuite à défaire, comme un analyseur XML serait: Tom & Jerry Si c'est le dernier, désolé, je ne sais pas comment le faire avec les outils de texte Unix. –

+0

@Paul 's/&/\ &/g', même chose pour' " 'etc, bien sûr, il ne généralisera pas pour les entités définies par l'utilisateur, etc. – 13ren

Répondre

51

Avez-vous vraiment doivent utiliser uniquement les outils? Ils ne sont pas conçus pour le traitement XML, et bien qu'il est possible d'obtenir quelque chose qui fonctionne bien la plupart du temps, il échouera sur les cas de pointe, 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 

sur ubuntu/debian, xml_grep est dans le paquet xml-brindille-outils.

+0

Des instructions d'installation serrées seraient parfaites pour xml_grep –

+0

sudo apt-get install xml-twig-tools – FredFury

0

Que diriez-vous:

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

UUOC. 'grep ' ghostdog74

+0

@ghost * mais mais mais, je pense que c'est plus propre/plus agréable/pas trop gaspillé/mon privilège de gaspiller des processus! * Http://partmaps.org/era/unix/award.html#cat (en fait, je pense qu'il est plus facile d'éditer le nom de fichier, car plus proche du début) – 13ren

+3

Si vous utilisez ' Thor

11
grep '<job' file_name | cut -f2 -d">"|cut -f1 -d"<" 
+0

seulement s'il échoue si les balises sont sur des lignes séparées – ghostdog74

+7

Il y a environ une douzaine d'autres façons que le XML bien formé peut faire échouer. –

6

il suffit d'utiliser awk, pas besoin d'autres outils externes. Ci-dessous fonctionne si vos étiquettes souhaitées apparaît en 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 
+0

'' est valide, mais votre script ne le reconnaît pas. '

+3

Il existe un nombre important d'outils différents qui utilisent la notation XPath standard pour extraire des informations de XML - 'xmlstarlet' n'en est qu'un. D'autres incluent 'xmllint',' xpath', etc. Voir http://stackoverflow.com/questions/15461737/how-to-execute-xpath-one-liners-from-shell – tripleee

8

S'il vous plaît ne pas utiliser l'analyse syntaxique basé sur la ligne et regex sur XML. C'est une mauvaise idée. Vous pouvez avoir un XML sémantiquement identique avec un formatage différent, et l'analyse syntaxique basée sur les regex et les lignes ne peut tout simplement pas y faire face.

choses comme les balises unaire et retour à la ligne variable - ces extraits 'dire 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 faire un analyseur à base regex/ligne est difficile? Heureusement, vous n'en avez pas besoin. Beaucoup de langages de script ont au moins une, parfois plus d'options d'analyseur.

Comme une affiche précédente a fait allusion à - xml_grep est disponible. C'est en fait un outil basé sur la bibliothèque Perl XML::Twig. Cependant, ce qu'il fait est d'utiliser des 'expressions xpath' pour trouver quelque chose, et différencie la structure du document, les attributs et le 'contenu'.

E.g.:

xml_grep 'job' jobs.xml --text_only 

Cependant, dans l'intérêt de faire de meilleures réponses, voici quelques exemples de «rouler votre propre en fonction de vos données source:

Première façon:

utilisation twig handlers qui attire des éléments d'un type particulier et agit sur eux. 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 jeter « transformés » XML lorsque vous travaillez avec de gros fichiers, 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 d'entrée (redirigée ou spécifié via commandline ./myscript somefile.xml) et le processus il - chaque élément job, il va extraire et imprimer tout texte associé. (Vous pourriez vouloir print $_ -> text,"\n" pour insérer un saut de ligne).

Parce qu'il est correspondant à des éléments « d'emploi », il va également correspondre à des éléments d'emploi imbriqués:

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

va vous donner deux fois, mais imprimer une partie de la sortie deux fois trop. Vous pouvez cependant faire correspondre /job à la place si vous préférez. Utilement - cela vous permet par exemple imprime et supprime un élément ou copie et colle celui modifiant la structure XML.

Alternativement - Parse d'abord, et 'print' basée sur la structure:

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

Comme job est votre élément racine, tout ce que nous devons faire est d'imprimer le texte de celui-ci.

Mais nous pouvons être un peu plus exigeants, et chercher job ou /job et impression qui spécifiquement à la place:

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

Vous pouvez utiliser XML::Twig de l'option pretty_print reformater votre XML aussi:

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

Il existe une variété d'options de format de sortie, mais pour un langage XML plus simple (comme le vôtre), la plupart seront très similaires.

0

Un peu tard pour le spectacle.

xmlcutty ARRÊTE nœuds 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> 

Les noms d'argument path le chemin d'accès à l'élément que vous voulez découper. Dans ce cas, puisque nous ne sommes pas intéressés par les balises du tout, on renomme l'étiquette à \n, donc nous obtenons une belle liste:

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

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

2

Utilisation sed commande:

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 - suppression de l'impression toutes les lignes
e - Script

/<pattern_to_find>/ - trouve les lignes qui contiennent modèle spécifié ce qui pourrait être par exemple <heading>

suivant fait partie de substitution s///p qui supprime tout sauf la valeur désirée où / est remplacé par # pour une meilleure lisibilité:

s#\s*<[^>]*>\s*##gp
\s* - comprend-espaces blancs si exist (même à la fin)
<[^>]*> représente <xml_tag> comme regex non avide autre cause de <.*?> ne fonctionne pas pour sed
g - remplace tout, par exemple fermeture xml </xml_tag> tag

Questions connexes