Idéalement, ce que je voudrais être en mesure de faire est :
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Idéalement, ce que je voudrais être en mesure de faire est :
cat xhtmlfile.xhtml |
getElementViaXPath --path='/html/head/title' |
sed -e 's%(^<title>|</title>$)%%g' > titleOfXHTMLPage.txt
Après quelques recherches pour la traduction entre les formats Linux et Windows des chemins de fichiers dans les fichiers XML, j'ai trouvé des tutoriels et des solutions intéressantes sur :
Bien qu'il existe un certain nombre d'utilitaires de console prêts à l'emploi qui peuvent faire ce que vous voulez, il sera probablement plus rapide d'écrire quelques lignes de code dans un langage de programmation polyvalent tel que Python, que vous pourrez facilement étendre et adapter à vos besoins.
Voici un script python qui utilise lxml
pour l'analyse syntaxique - il prend le nom d'un fichier ou d'une URL comme premier paramètre, une expression XPath comme second paramètre, et imprime les chaînes/nœuds correspondant à l'expression donnée.
#!/usr/bin/env python
import sys
from lxml import etree
tree = etree.parse(sys.argv[1])
xpath_expression = sys.argv[2]
# a hack allowing to access the
# default namespace (if defined) via the 'p:' prefix
# E.g. given a default namespaces such as 'xmlns="http://maven.apache.org/POM/4.0.0"'
# an XPath of '//p:module' will return all the 'module' nodes
ns = tree.getroot().nsmap
if ns.keys() and None in ns:
ns['p'] = ns.pop(None)
# end of hack
for e in tree.xpath(xpath_expression, namespaces=ns):
if isinstance(e, str):
print(e)
else:
print(e.text and e.text.strip() or etree.tostring(e, pretty_print=True))
lxml
peut être installé avec pip install lxml
. Sur ubuntu, vous pouvez utiliser sudo apt install python-lxml
.
python xpath.py myfile.xml "//mynode"
lxml
accepte également une URL en entrée :
python xpath.py http://www.feedforall.com/sample.xml "//link"
Note : Si votre XML possède un espace de noms par défaut sans préfixe (par ex.
xmlns=http://abc...
), vous devez alors utiliser l'optionp
(fourni par le "hack") dans vos expressions, par exemple//p:module
pour obtenir les modules d'unpom.xml
fichier. Au cas où lep
est déjà mappé dans votre XML, alors vous devrez modifier le script pour utiliser un autre préfixe.
Un script unique qui sert le but étroit d'extraire les noms de modules d'un fichier apache maven. Notez comment le nom du nœud ( module
) est préfixé avec l'espace de noms par défaut {http://maven.apache.org/POM/4.0.0}
:
pom.xml :
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modules>
<module>cherries</module>
<module>bananas</module>
<module>pears</module>
</modules>
</project>
module_extractor.py :
from lxml import etree
for _, e in etree.iterparse(open("pom.xml"), tag="{http://maven.apache.org/POM/4.0.0}module"):
print(e.text)
La méthode de Yuzem peut être améliorée en inversant l'ordre de la méthode de la <
et >
signes dans le rdom
et les affectations de variables, de sorte que :
rdom () { local IFS=\> ; read -d \< E C ;}
devient :
rdom () { local IFS=\< ; read -d \> C E ;}
Si l'analyse syntaxique n'est pas effectuée de cette manière, la dernière balise du fichier XML n'est jamais atteinte. Cela peut être problématique si vous avez l'intention de sortir un autre fichier XML à la fin de la balise while
boucle.
Bien qu'il semble que "ne jamais analyser XML, JSON... à partir de bash sans un outil approprié" soit un conseil judicieux, je ne suis pas d'accord. Si c'est un travail d'appoint, il n'est pas nécessaire de chercher l'outil approprié, puis de l'apprendre... Awk peut le faire en quelques minutes. Mes programmes doivent travailler sur tous les types de données mentionnés ci-dessus et plus encore. Je ne veux pas tester 30 outils pour analyser les 5-7-10 formats différents dont j'ai besoin si je peux résoudre le problème avec Awk en quelques minutes. Je ne me soucie pas de XML, JSON ou autre ! J'ai besoin d'une solution unique pour tous ces formats.
Par exemple, mon programme SmartHome gère nos maisons. Ce faisant, il lit une pléthore de données dans trop de formats différents que je ne peux pas contrôler. Je n'utilise jamais d'outils dédiés et appropriés car je ne veux pas passer plus de quelques minutes à lire les données dont j'ai besoin. Avec les ajustements de FS et RS, cette solution awk fonctionne parfaitement pour tout format textuel. Mais, ce n'est peut-être pas la bonne réponse lorsque votre tâche principale est de travailler principalement avec des tas de données dans ce format !
Le problème de l'analyse du XML à partir de bash, je l'ai rencontré hier. Voici comment je procède pour tout format de données hiérarchique. En bonus - j'assigne les données directement aux variables dans un script bash.
Pour faciliter la lecture, je vais présenter la solution par étapes. A partir des données du test OP, j'ai créé un fichier : test.xml
Parser ledit XML en bash et extraire les données en 90 chars :
awk 'BEGIN { FS="<|>"; RS="\n" }; /host|username|password|dbname/ { print $2, $4 }' test.xml
J'utilise normalement la version la plus lisible car elle est plus facile à modifier dans la vie réelle, car j'ai souvent besoin de tester différemment :
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2,$4}' test.xml
Je ne me soucie pas de la façon dont le format est appelé. Je ne cherche que la solution la plus simple. Dans ce cas particulier, je peux voir à partir des données que newline est le séparateur d'enregistrement (RS) et <> délimite les champs (FS). Dans mon cas original, j'avais une indexation compliquée de 6 valeurs dans deux enregistrements, les mettre en relation, trouver quand les données existent plus les champs (enregistrements) peuvent ou non exister. Il a fallu 4 lignes d'awk pour résoudre parfaitement le problème. Donc, adaptez l'idée à chaque besoin avant de l'utiliser !
La deuxième partie vérifie simplement s'il y a une chaîne de caractères souhaitée dans une ligne (RS) et, si c'est le cas, imprime les champs nécessaires (FS). Ce qui précède m'a pris environ 30 secondes pour copier et adapter la dernière commande que j'ai utilisée de cette façon (4 fois plus longue). Et c'est tout ! Fait en 90 caractères.
Mais j'ai toujours besoin de placer les données dans des variables dans mon script. Je teste d'abord les constructions comme ceci :
awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml
Dans certains cas, j'utilise printf au lieu de print. Lorsque je vois que tout se passe bien, je finis simplement d'assigner des valeurs aux variables. Je sais que beaucoup pensent que "eval" est "diabolique", pas besoin de commenter :) Cette astuce fonctionne parfaitement sur mes quatre réseaux depuis des années. Mais continuez à apprendre si vous ne comprenez pas pourquoi c'est une mauvaise pratique ! En incluant les assignations de variables bash et un espacement suffisant, ma solution nécessite 120 caractères pour tout faire.
eval $( awk 'BEGIN { FS="<|>"; RS="\n" }; { if ($0 ~ /host|username|password|dbname/) print $2"=\""$4"\"" }' test.xml ); echo "host: $host, username: $username, password: $password dbname: $dbname"
Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.