Equivalente Grep y Sed para el procesamiento de línea de comando XML

147

Al hacer scripts de shell, generalmente los datos estarán en archivos de registros de una sola línea como csv. Es realmente sencillo manejar estos datos con grepy sed. Pero tengo que lidiar con XML a menudo, por lo que realmente me gustaría una forma de acceso de script a esos datos XML a través de la línea de comandos. ¿Cuáles son las mejores herramientas?

Joseph Holsten
fuente
xml_grep está bien para grepping, como se indica en stackoverflow.com/a/2222224/871134
Deleplace

Respuestas:

105

He encontrado que xmlstarlet es bastante bueno en este tipo de cosas.

http://xmlstar.sourceforge.net/

También debería estar disponible en la mayoría de los repositorios de distro. Un tutorial introductorio está aquí:

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

Russ
fuente
1
Pensé en señalar que hay binarios de Windows disponibles en el sitio de Sourceforge.
Steve Bennett
Sin embargo, no es compatible con XQuery, por lo que puedo decir.
Steve Bennett
@SteveBennett, de hecho, no lo hace, pero las características que agrega sobre XPath sin procesar son lo suficientemente buenas como para que sea competitivo con "grep and sed". Si quieres la bondad elegante de XQuery ... bueno, eso es más como un equivalente XML a perl o awk. :)
Charles Duffy
36

Algunas herramientas prometedoras:

  • nokogiri : análisis de DOMs HTML / XML en ruby ​​usando selectores XPath y CSS

  • hpricot : en desuso

  • fxgrep : utiliza su propia sintaxis similar a XPath para consultar documentos. Escrito en SML, por lo que la instalación puede ser difícil.

  • LT XML : XML Toolkit derivado de SGML, incluyendo herramientas sggrep, sgsort, xmlnormy otros. Utiliza su propia sintaxis de consulta. La documentación es muy formal. Escrito en C. LT XML 2 reclama el soporte de XPath, XInclude y otros estándares W3C.

  • xmlgrep2 : búsqueda simple y potente con XPath. Escrito en Perl usando XML :: LibXML y libxml2.

  • XQSharp : admite XQuery, la extensión de XPath. Escrito para .NET Framework.

  • xml-coreutils : kit de herramientas de Laird Breyer equivalente a GNU coreutils. Discutido en un interesante ensayo sobre lo que debe incluir el conjunto de herramientas ideal.

  • xmldiff : herramienta simple para comparar dos archivos xml.

  • xmltk : no parece tener un paquete en debian, ubuntu, fedora o macports, no ha tenido un lanzamiento desde 2007 y utiliza la automatización de compilación no portátil.

xml-coreutils parece el mejor documentado y más orientado a UNIX.

Joseph Holsten
fuente
1
¿No podría crear un script de envoltura para el programa Ruby y pasar la matriz de argumentos en el script a hpricot? Por ejemplo, en un script de shell PHP, algo como lo siguiente debería funcionar: <? Php / path / to / hpricot $ argv?>
alastairs
25

A la excelente lista de Joseph Holsten, agrego el script de línea de comandos xpath que viene con la biblioteca Perl XML :: XPath. Una excelente manera de extraer información de archivos XML:

 xpath -q -e '/entry[@xml:lang="fr"]' *xml
bortzmeyer
fuente
3
Esto se instala por defecto en osx, pero sin -q -eopciones. Ejemplo, obtenga el valor del atributo "paquete" del nodo "manifiesto" en "AndroidManifest.xml":xpath AndroidManifest.xml 'string(/manifest/@package)' 2> /dev/null
antonj
25

También hay xml2y 2xmlpar. Permitirá que las herramientas habituales de edición de cadenas procesen XML.

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

PD También hay html2/ 2html.

Vi.
fuente
@ Joseph Holsten Sí. Permite hackear con XML sin pensar en las cosas de XPath.
Vi.
¡Agradable! Me había centrado en herramientas que no usan un formato intermedio, pero la idea de una representación de xml de alta fidelidad y orientada a líneas parece una excelente manera de seguir usando grep y sed reales. ¿Has probado pyxie? ¿Cómo se compara? ¿Alguna otra representación orientada a líneas? ¿Consideraría esto mejor que simplemente reemplazar las nuevas líneas xml con una entidad (& # 10;)? Esto le permitiría pegar registros en la misma línea al menos. Ah, ¿y podrías editar tu publicación para incluir un enlace al proyecto?
Joseph Holsten
@Joseph Holsten No, no creo que el formato pyxie sea más útil que el formato xml2. xml2 proporciona "ruta completa" en elementos XML anidados, por lo que permite una mayor coincidencia y sustitución orientada a líneas. También 2xmlpuede recrear fácilmente XML a partir de resultados parciales (filtrados) xml2.
Vi.
55
+1 No puedo votar esto lo suficiente ... cat foo.xml | xml2 | grep /bar | 2xml- te da la misma estructura que el original, pero todos los elementos han sido eliminados excepto los elementos de "barra". Increíble.
mogsie
14

Puedes usar xmllint:

xmllint --xpath //title books.xml

Debe incluirse con la mayoría de las distribuciones, y también incluye Cygwin.

$ xmllint --version
xmllint: using libxml version 20900

Ver:

$ 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
fuente
2
No hay --xpathargumento para xmllint: manpagez.com/man/1/xmllint
Variable miserable
1
@MiserableVariable: la página del manual es incorrecta. Acabo de mirar la página de manual de mi versión: el argumento xpath no está en la lista. Este es un error de documentación. Intente ejecutar el programa, en su lugar.
Dave Jarvis
2
@MiserableVariable --xpathes una adición bastante reciente y, por ejemplo, no en las versiones de RHEL 6 de xmllint.
Daniel Beck
2
Para ser más precisos, xmllint --xpathse introdujo en libxml2 2.7.7 (en 2010).
marbu
9

Si está buscando una solución en Windows, Powershell tiene una funcionalidad incorporada para leer y escribir XML.

test.xml:

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

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

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

Arcilla
fuente
Luchó con varias herramientas de Linux durante unas horas antes de recurrir a Powershell. Me sorprende que esto sea tan difícil: la línea cmd de Linux normalmente es realmente buena, pero parece que hay un agujero aquí. Nota: El caso de uso para mí fue: 1) localizar nodos por xpath, 2) eliminar si se encuentra, 3) agregar nuevos nodos, 4) guardar el archivo. Estaba actualizando un montón de configuraciones solr. Si alguien sabe de una manera fácil / confiable de hacer esto, soy todo oídos
Richard Hauer
Wow, esto realmente va de puntillas a la línea de una solución aceptable. Pero honestamente, ¡probablemente lo aceptaría si lo pareciera xps $doc .root.one xps $doc 'AppendChild("three")'y xps $doc '.three.set_InnerText("And don't you forget it!")', lo cual es claramente inferior!
Joseph Holsten
6

Depende exactamente de lo que quieras hacer.

XSLT puede ser el camino a seguir, pero hay una curva de aprendizaje. Pruebe xsltproc y tenga en cuenta que puede entregar los parámetros.

Adrian Mouat
fuente
4

También hay una saxon-lintlínea de comandos con la capacidad de usar XPath 3.0 / XQuery 3.0. (Otras herramientas de línea de comandos usan XPath 1.0).

EJEMPLOS

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

XQuery podría ser una buena solución. Es (relativamente) fácil de aprender y es un estándar W3C.

Recomendaría XQSharp para un procesador de línea de comando.

Oliver Hallam
fuente
1
BaseX también tiene un procesador XQuery de línea de comandos (además de su modo de base de datos), y se mantiene actualizado con versiones innovadoras del estándar (siguiendo el borrador en evolución de XQuery 3.0 bastante de cerca).
Charles Duffy
3

Primero usé xmlstarlet y todavía lo uso. Cuando la consulta se vuelve difícil, necesito el soporte de las funciones xpath2 y xquery de XML. Me dirijo a xidel http://www.videlibri.de/xidel.html

truthadjustr
fuente
1

Grep equivalente

Puede definir una función bash, digamos "xp" ("xpath") que envuelve algún código python3. Para usarlo necesitas instalar python3 y python-lxml. Beneficios:

  1. coincidencia de expresiones regulares que le falta, por ejemplo, en xmllint.
  2. Usar como filtro (en una tubería) en la línea de comando

Es fácil y poderoso de usar así:

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 () se parece a esto:

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

Equivalente de Sed

Considere usar xq que le brinda todo el poder del "lenguaje de programación" jq. Si tiene instalado python-pip, puede instalar xq con pip install yq , luego, en el siguiente ejemplo, reemplazaremos "Mantener cuentas" por "Mantener cuentas 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
Matusalén-0
fuente
-1

JEdit tiene un complemento llamado "XQuery" que proporciona funcionalidad de consulta para documentos XML.

No es exactamente la línea de comando, ¡pero funciona!

Ben
fuente
Si bien es probable que JEdit tenga una forma de buscar en un archivo, eso no lo convierte en un competidor grep(1).
Joseph Holsten