2010-02-08 14 views
31

ho un file XML con il contenuto:Estrazione di dati da un semplice file XML

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

Ho bisogno di un modo per estrarre ciò che è nei <job..></job> tag, PROGRAMMAZIONE in questo caso. Questo dovrebbe essere fatto sul prompt dei comandi di linux, usando grep/sed/awk.

+0

Se il file XML contiene questo: < xmlns = lavoro "http://www.sample.com/" > Tom & Jerry vorreste il risultato di avere XML escape lasciato solo: Tom & Jerry o vorresti la fuga per essere annullata, come un parser XML sarebbe: Tom & Jerry Se è il secondo, mi dispiace, non so come farlo con gli strumenti di testo Unix. –

+0

@Paul 's/&/\ &/g', stesso per' " 'ecc, ovviamente non sarà generalizzato per entità definite dall'utente ecc. – 13ren

risposta

51

si ha realmente devono uso solo quegli strumenti? Non sono progettati per l'elaborazione XML, e anche se è possibile ottenere qualcosa che funziona bene la maggior parte del tempo, non riuscirà a casi limite, come la codifica, interruzioni di riga, ecc

vi consiglio xml_grep:

xml_grep 'job' jobs.xml --text_only 

che fornisce l'output:

programming 

su Ubuntu/Debian, xml_grep è nel pacchetto xml-ramoscello-tools.

+0

Le istruzioni di installazione strette sarebbero grandiose per xml_grep –

+0

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

0

ne dite:

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

UUOC. 'grep ' ghostdog74

+0

@ghost * ma ma, penso che sia più pulito/più bello/non molto di uno spreco/il mio privilegio di sprecare i processi! * Http://partmaps.org/era/unix/award.html#cat (in realtà, penso che sia più facile modificare il nome del file, perché più vicino all'inizio) – 13ren

+3

Se si usa ' Thor

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

solo che non riesce se i tag sono su righe separate – ghostdog74

+7

Ci sono circa una dozzina di altri modi in cui l'XML ben formato può fallire. –

6

basta usare awk, non è necessario altri strumenti esterni. Qui sotto funziona se i tag desiderati appaiono in 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

'' è valido, ma il tuo script non lo riconosce. '

+3

Esiste un numero significativo di strumenti diversi che utilizzano la notazione XPath standard per estrarre informazioni da XML - 'xmlstarlet' è solo uno. Altri includono 'xmllint',' xpath', ecc. Vedi http://stackoverflow.com/questions/15461737/how-to-execute-xpath-one-liners-from-shell – tripleee

8

prega di non utilizzare la linea e l'analisi basata su XML regex. È una cattiva idea È possibile avere un XML semanticamente identico con una formattazione diversa, e l'espressione regolare e l'analisi basata sulla linea semplicemente non possono farcela.

Cose come i tag unari e la linea di confezionamento variabile - questi frammenti 'dire' la stessa cosa:

<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> 

Speriamo che questo rende chiaro perché facendo un parser basato regex/linea è difficile? Fortunatamente, non è necessario. Molti linguaggi di scripting hanno almeno una, a volte più opzioni di parser.

Come accennato in precedenza a un poster - xml_grep è disponibile. Questo è in realtà uno strumento basato sulla libreria perl XML::Twig perl. Tuttavia, ciò che fa è usare "espressioni xpath" per trovare qualcosa e differenzia tra struttura del documento, attributi e "contenuto".

E.g.:

xml_grep 'job' jobs.xml --text_only 

Tuttavia, nell'interesse di fare risposte migliori, ecco un paio di esempi di 'roll your own', sulla base di dati di origine:

Primo modo:

Usa twig handlers che cattura gli elementi di un tipo particolare e agisce su di loro. Il vantaggio di farlo in questo modo è che analizza l'XML "mentre vai" e ti consente di modificarlo in volo se necessario. Ciò è particolarmente utile per scartare XML 'elaborati', quando si lavora con file di grandi dimensioni, utilizzando purge o flush:

#!/usr/bin/perl 

use strict; 
use warnings; 

use XML::Twig; 

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

che utilizzerà <> di prendere in ingresso (di sottofondo, o specificato tramite riga di comando ./myscript somefile.xml) e di processo it - ogni elemento job, estrarrà e stamperà qualsiasi testo associato. (Potrebbe essere necessario print $_ -> text,"\n" per inserire un avanzamento riga).

Perché è corrispondenza su elementi 'lavoro', sarà anche abbinare su elementi di lavoro nidificati:

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

corrisponderanno due volte, ma la stampa alcuni dei uscita due volte troppo. Puoi comunque, abbinare su /job se preferisci. Utilmente: ti consente, ad es. stampa ed elimina un elemento o copia e incolla uno modificando la struttura XML.

alternativa - Analizza prima, e 'Stampa' sulla base della struttura:

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

Come job è il vostro elemento radice, tutto quello che dobbiamo fare è stampare il testo di esso.

Ma possiamo essere un po 'più esigenti, e cercate job o /job e di stampa che specificamente invece:

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

È possibile utilizzare XML::Twig s opzione pretty_print riformattare il XML troppo:

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

C'è una varietà di opzioni di formato di output, ma per XML più semplice (come il tuo) la maggior parte sembrerà abbastanza simile.

0

Un po 'in ritardo per lo spettacolo.

xmlcutty taglia fuori i nodi da 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> 

I nomi degli argomenti path il percorso per l'elemento che si desidera ritagliare. In questo caso, dal momento che non siamo interessati nei tag a tutti, rinominiamo il tag \n, in modo da ottenere una bella lista:

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

nota, che l'XML non era valida per cominciare (radice elemento). xmlcutty può funzionare anche con XML leggermente spezzato.

2

Utilizzando sed comando:

Esempio:

$ 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 

Spiegazione:

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

n - di script

/<pattern_to_find>/ - - la stampa di tutte le linee di
e sopprimere trova le righe che contengono specificati modello quello che potrebbe essere per esempio <heading>

prossimo è la sostituzione parte s///p che rimuove tutto tranne valore desiderato in cui / è sostituito con # per una migliore leggibilità:

s#\s*<[^>]*>\s*##gp
\s* - comprende il bianco-spazi se esistono (lo stesso alla fine)
<[^>]*> rappresenta <xml_tag> come causa alternativa regolare non avida <.*?> non funziona per sed
g - sostituisce tutto ad es. Chiusura xml </xml_tag> tag