Grep et Sed équivalent pour le traitement de ligne de commande XML

147

Lorsque vous effectuez des scripts shell, les données se trouvent généralement dans des fichiers d'enregistrements sur une seule ligne tels que csv. C'est vraiment simple de gérer ces données avec grepet sed. Mais je dois souvent m'occuper de XML, donc j'aimerais vraiment avoir un moyen d'accéder par script à ces données XML via la ligne de commande. Quels sont les meilleurs outils?

Joseph Holsten
la source
xml_grep est parfait pour grepping, comme indiqué dans stackoverflow.com/a/2222224/871134
Deleplace

Réponses:

105

J'ai trouvé que xmlstarlet était assez bon dans ce genre de choses.

http://xmlstar.sourceforge.net/

Devrait également être disponible dans la plupart des référentiels de distribution. Un tutoriel d'introduction est ici:

http://www.ibm.com/developerworks/library/x-starlet.html

Russ
la source
1
Je pensais que je soulignerais qu'il existe des binaires Windows disponibles sur le site Sourceforge.
Steve Bennett
Pour autant que je sache, ne prend pas en charge XQuery.
Steve Bennett
@SteveBennett en effet pas, mais les fonctionnalités qu'il ajoute en plus de XPath brut sont suffisamment bonnes pour le rendre compétitif avec "grep and sed". Si vous voulez la fantaisie, la bonté de XQuery ... eh bien, cela ressemble plus à un équivalent XML à perl ou awk. :)
Charles Duffy
36

Quelques outils prometteurs:

  • nokogiri : analyse des DOM HTML / XML dans ruby ​​à l'aide des sélecteurs XPath et CSS

  • hpricot : obsolète

  • fxgrep : utilise sa propre syntaxe de type XPath pour interroger des documents. Écrit en SML, l'installation peut donc être difficile.

  • LT XML : boîte à outils XML dérivé d'outils de SGML, y compris sggrep, sgsort, xmlnormet d' autres. Utilise sa propre syntaxe de requête. La documentation est très formelle. Écrit en C. LT XML 2 revendique la prise en charge de XPath, XInclude et d'autres normes W3C.

  • xmlgrep2 : recherche simple et puissante avec XPath. Écrit en Perl en utilisant XML :: LibXML et libxml2.

  • XQSharp : prend en charge XQuery, l'extension de XPath. Rédigé pour le .NET Framework.

  • xml-coreutils : boîte à outils de Laird Breyer équivalente à GNU coreutils. Discuté dans un essai intéressant sur ce que la boîte à outils idéale devrait inclure.

  • xmldiff : outil simple pour comparer deux fichiers xml.

  • xmltk : ne semble pas avoir de paquet dans debian, ubuntu, fedora ou macports, n'a pas eu de version depuis 2007 et utilise une automatisation de construction non portable.

xml-coreutils semble le mieux documenté et le plus orienté UNIX.

Joseph Holsten
la source
1
Ne pourriez-vous pas créer un script wrapper pour le programme Ruby et passer le tableau des arguments du script à hpricot? Par exemple, dans un script shell PHP, quelque chose comme ce qui suit devrait fonctionner: <? Php / path / to / hpricot $ argv?>
alastairs
25

À l'excellente liste de Joseph Holsten, j'ajoute le script de ligne de commande xpath fourni avec la bibliothèque Perl XML :: XPath. Un excellent moyen d'extraire des informations à partir de fichiers XML:

 xpath -q -e '/entry[@xml:lang="fr"]' *xml
Bortzmeyer
la source
3
Ceci est installé par défaut dans osx, mais sans -q -eoptions. Exemple, obtenez la valeur de l'attribut "package" à partir du nœud "manifest" dans "AndroidManifest.xml":xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
antonj
25

Il y a aussi xml2et 2xmlpaire. Cela permettra aux outils d'édition de chaînes habituels de traiter XML.

Exemple. q.xml:

<?xml version="1.0"?>
<foo>
    text
    more text
    <textnode>ddd</textnode><textnode a="bv">dsss</textnode>
    <![CDATA[ asfdasdsa <foo> sdfsdfdsf <bar> ]]>
</foo>

xml2 < q.xml

/foo=
/foo=   text
/foo=   more text
/foo=   
/foo/textnode=ddd
/foo/textnode
/foo/textnode/@a=bv
/foo/textnode=dsss
/foo=
/foo=    asfdasdsa <foo> sdfsdfdsf <bar> 
/foo=

xml2 < q.xml | grep textnode | sed 's!/foo!/bar/baz!' | 2xml

<bar><baz><textnode>ddd</textnode><textnode a="bv">dsss</textnode></baz></bar>

PS Il y a aussi html2/ 2html.

Vi.
la source
@Joseph Holsten Oui. Il permet de pirater avec XML sans penser aux choses XPath.
Vi.
Agréable! Je m'étais concentré sur des outils qui n'utilisent pas de format intermédiaire, mais l'idée d'une représentation haute fidélité orientée ligne de xml semble être un excellent moyen de continuer à utiliser de vrais grep et sed. Avez-vous essayé pyxie? Comment cela se compare-t-il? D'autres représentations orientées lignes? Considérez-vous cela mieux que de simplement remplacer les nouvelles lignes xml par une entité (& # 10;)? Cela vous permettrait au moins de coller des enregistrements sur la même ligne. Oh, et pourriez-vous modifier votre message pour inclure un lien vers le projet?
Joseph Holsten
@Joseph Holsten Non, je ne pense pas que le format pyxie serait plus utile que le format xml2. xml2 fournit un "chemin complet" dans les éléments XML imbriqués, permettant ainsi davantage de correspondance et de substitution orientées lignes. 2xmlPeut également recréer facilement du XML à partir d'une xml2sortie partielle (filtrée) .
Vi.
5
+1 Je ne peux pas assez voter pour cela ... cat foo.xml | xml2 | grep /bar | 2xml- vous donne la même structure que l'original, mais tous les éléments ont été supprimés sauf les éléments "barre". Impressionnant.
mogsie
14

Vous pouvez utiliser xmllint:

xmllint --xpath //title books.xml

Doit être fourni avec la plupart des distributions et est également fourni avec Cygwin.

$ xmllint --version
xmllint: using libxml version 20900

Voir:

$ xmllint
Usage : xmllint [options] XMLfiles ...
        Parse the XML files and output the result of the parsing
        --version : display the version of the XML library used
        --debug : dump a debug tree of the in-memory document
        ...
        --schematron schema : do validation against a schematron
        --sax1: use the old SAX1 interfaces for processing
        --sax: do not build a tree but work just at the SAX level
        --oldxml10: use XML-1.0 parsing rules before the 5th edition
        --xpath expr: evaluate the XPath expression, inply --noout
Dave Jarvis
la source
2
Il n'y a aucun --xpathargument à xmllint: manpagez.com/man/1/xmllint
Variable misérable
1
@MiserableVariable: la page de manuel est incorrecte. Je viens de regarder la page de manuel de ma version: l'argument xpath n'est pas répertorié. Ceci est une erreur de documentation. Essayez plutôt d'exécuter le programme.
Dave Jarvis
2
@MiserableVariable --xpathest un ajout assez récent et par exemple pas dans les versions RHEL 6 de xmllint.
Daniel Beck
2
Pour être plus précis, a xmllint --xpathété introduit dans libxml2 2.7.7 (en 2010).
marbu
9

Si vous recherchez une solution sous Windows, Powershell dispose d'une fonctionnalité intégrée pour lire et écrire du XML.

test.xml:

<root>
  <one>I like applesauce</one>
  <two>You sure bet I do!</two>
</root>

Script PowerShell:

# load XML file into local variable and cast as XML type.
$doc = [xml](Get-Content ./test.xml)

$doc.root.one                                   #echoes "I like applesauce"
$doc.root.one = "Who doesn't like applesauce?"  #replace inner text of <one> node

# create new node...
$newNode = $doc.CreateElement("three")
$newNode.set_InnerText("And don't you forget it!")

# ...and position it in the hierarchy
$doc.root.AppendChild($newNode)

# write results to disk
$doc.save("./testNew.xml")

testNew.xml:

<root>
  <one>Who likes applesauce?</one>
  <two>You sure bet I do!</two>
  <three>And don't you forget it!</three>
</root>

Source: /server/26976/update-xml-from-the-command-line-windows

Argile
la source
lutté avec divers outils Linux pendant quelques heures avant de recourir à Powershell. Je suis surpris que ce soit si difficile - linux cmd-line est normalement vraiment bon mais il semble y avoir un trou ici. Remarque: le cas d'utilisation pour moi était: 1) localiser les nœuds par xpath, 2) supprimer si trouvé, 3) ajouter de nouveaux nœuds, 4) enregistrer le fichier. Je mettais à jour un tas de configs solr. Si quelqu'un connaît un moyen facile / fiable de le faire, je suis tout à fait à l'écoute
Richard Hauer
Wow, c'est vraiment la pointe des pieds jusqu'à la ligne d'une solution acceptable. Mais honnêtement, je l'accepterais probablement si cela ressemblait à xps $doc .root.one xps $doc 'AppendChild("three")'et xps $doc '.three.set_InnerText("And don't you forget it!")', ce qui est clairement inférieur!
Joseph Holsten
6

Cela dépend exactement de ce que vous voulez faire.

XSLT est peut-être la voie à suivre, mais il y a une courbe d'apprentissage. Essayez xsltproc et notez que vous pouvez transmettre des paramètres.

Adrian Mouat
la source
4

Il existe également une saxon-lintligne de commande avec la possibilité d'utiliser XPath 3.0 / XQuery 3.0. (D'autres outils de ligne de commande utilisent XPath 1.0).

EXEMPLES :

http / html:

$ saxon-lint --html --xpath 'count(//a)' http://stackoverflow.com/q/91791
328

xml:

$ saxon-lint --xpath '//a[@class="x"]' file.xml
Gilles Quenot
la source
3

XQuery pourrait être une bonne solution. C'est (relativement) facile à apprendre et c'est une norme du W3C.

Je recommanderais XQSharp pour un processeur de ligne de commande.

Oliver Hallam
la source
1
BaseX dispose également d'un processeur XQuery en ligne de commande (en plus de son mode base de données) et reste à jour avec les versions de pointe de la norme (en suivant de très près le projet évolutif de XQuery 3.0).
Charles Duffy
3

J'ai d'abord utilisé xmlstarlet et je l'utilise toujours. Lorsque la requête devient difficile, j'ai besoin de la prise en charge des fonctionnalités xpath2 et xquery de XML.Je me tourne vers xidel http://www.videlibri.de/xidel.html

truthadjustr
la source
1

Equivalent Grep

Vous pouvez définir une fonction bash, par exemple "xp" ("xpath") qui encapsule du code python3. Pour l'utiliser, vous devez installer python3 et python-lxml. Avantages:

  1. regex correspondant qui vous manque par exemple xmllint.
  2. Utiliser comme filtre (dans un tube) sur la ligne de commande

C'est facile et puissant à utiliser comme ceci:

xmldoc=$(cat <<EOF
<?xml version="1.0" encoding="utf-8"?>
<job xmlns="http://www.sample.com/">programming</job>
EOF
)
selection='//*[namespace-uri()="http://www.sample.com/" and local-name()="job" and re:test(.,"^pro.*ing$")]/text()'
echo "$xmldoc" | xp "$selection"
# prints programming

xp () ressemble à ceci:

xp()
{ 
local selection="$1";
local xmldoc;
if ! [[ -t 0 ]]; then
    read -rd '' xmldoc;
else
    xmldoc="$2";
fi;
python3 <(printf '%b' "from lxml.html import tostring\nfrom lxml import etree\nfrom sys import stdin\nregexpNS = \"http://exslt.org/regular-expressions\"\ntree = etree.parse(stdin)\nfor e in tree.xpath('""$selection""', namespaces={'re':regexpNS}):\n  if isinstance(e, str):\n    print(e)\n  else:\n    print(tostring(e).decode('UTF-8'))") <<< "$xmldoc"
}

Équivalent Sed

Pensez à utiliser xq qui vous donne toute la puissance du "langage de programmation" jq. Si vous avez installé python-pip, vous pouvez installer xq avec pip install yq , puis dans l'exemple ci-dessous, nous remplaçons "Keep Accounts" par "Keep Accounts 2":

xmldoc=$(cat <<'EOF'
<resources>
    <string name="app_name">Keep Accounts</string>
    <string name="login">"login"</string>
    <string name="login_password">"password:"</string>
    <string name="login_account_hint">input to login</string>
    <string name="login_password_hint">input your password</string>
    <string name="login_fail">login failed</string>
</resources>
EOF
)
echo "$xmldoc" | xq '.resources.string = ([.resources.string[]|select(."#text" == "Keep Accounts") ."#text" = "Keep Accounts 2"])' -x
methuselah-0
la source
-1

JEdit a un plugin appelé "XQuery" qui fournit des fonctionnalités d'interrogation pour les documents XML.

Pas tout à fait la ligne de commande, mais ça marche!

Ben
la source
Bien que JEdit ait probablement un moyen de rechercher dans un fichier, cela n'en fait pas un concurrent grep(1).
Joseph Holsten