XML analysieren, um den Knotenwert im Bash-Skript abzurufen?


19

Ich möchte wissen, wie ich den Wert eines Knotens mit den folgenden Pfaden erhalten kann:

config/global/resources/default_setup/connection/host
config/global/resources/default_setup/connection/username
config/global/resources/default_setup/connection/password
config/global/resources/default_setup/connection/dbname

aus dem folgenden XML:

<?xml version="1.0"?>
<config>
    <global>
        <install>
            <date><![CDATA[Tue, 11 Dec 2012 12:31:25 +0000]]></date>
        </install>
        <crypt>
            <key><![CDATA[70e75d7969b900b696785f2f81ecb430]]></key>
        </crypt>
        <disable_local_modules>false</disable_local_modules>
        <resources>
            <db>
                <table_prefix><![CDATA[]]></table_prefix>
            </db>
            <default_setup>
                <connection>
                    <host><![CDATA[localhost]]></host>
                    <username><![CDATA[root]]></username>
                    <password><![CDATA[pass123]]></password>
                    <dbname><![CDATA[testdb]]></dbname>
                    <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
                    <model><![CDATA[mysql4]]></model>
                    <type><![CDATA[pdo_mysql]]></type>
                    <pdoType><![CDATA[]]></pdoType>
                    <active>1</active>
                </connection>
            </default_setup>
        </resources>
        <session_save><![CDATA[files]]></session_save>
    </global>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <frontName><![CDATA[admin]]></frontName>
                </args>
            </adminhtml>
        </routers>
    </admin>
</config>

Außerdem möchte ich diesen Wert der Variablen zur weiteren Verwendung zuweisen. Lass mich deine Idee wissen.


7
Verwenden Sie bash niemals, um strukturierte Bäume mit beliebigen Daten zu analysieren. Verwenden Sie einen echten XML-Parser. Ich empfehle XMLStarlet .
Chris Down

Antworten:


19

Verwenden von bashund xmllint(wie durch die Tags angegeben):

xmllint --version  #  xmllint: using libxml version 20703

# Note: Newer versions of libxml / xmllint have a --xpath option which 
# makes it possible to use xpath expressions directly as arguments. 
# --xpath also enables precise output in contrast to the --shell & sed approaches below.
#xmllint --help 2>&1 | grep -i 'xpath'

{
# the given XML is in file.xml
host="$(echo "cat /config/global/resources/default_setup/connection/host/text()" | xmllint --nocdata --shell file.xml | sed '1d;$d')"
username="$(echo "cat /config/global/resources/default_setup/connection/username/text()" | xmllint --nocdata --shell file.xml | sed '1d;$d')"
password="$(echo "cat /config/global/resources/default_setup/connection/password/text()" | xmllint --nocdata --shell file.xml | sed '1d;$d')"
dbname="$(echo "cat /config/global/resources/default_setup/connection/dbname/text()" | xmllint --nocdata --shell file.xml | sed '1d;$d')"
printf '%s\n' "host: $host" "username: $username" "password: $password" "dbname: $dbname"
}

# output
# host: localhost
# username: root
# password: pass123
# dbname: testdb

Für den Fall, dass es nur eine XML-Zeichenfolge gibt und die Verwendung einer temporären Datei vermieden werden soll, sind Dateideskriptoren der richtige Weg xmllint(der hier /dev/fd/3als Dateiargument angegeben wird):

set +H
{
xmlstr='<?xml version="1.0"?>
<config>
    <global>
        <install>
            <date><![CDATA[Tue, 11 Dec 2012 12:31:25 +0000]]></date>
        </install>
        <crypt>
            <key><![CDATA[70e75d7969b900b696785f2f81ecb430]]></key>
        </crypt>
        <disable_local_modules>false</disable_local_modules>
        <resources>
            <db>
                <table_prefix><![CDATA[]]></table_prefix>
            </db>
            <default_setup>
                <connection>
                    <host><![CDATA[localhost]]></host>
                    <username><![CDATA[root]]></username>
                    <password><![CDATA[pass123]]></password>
                    <dbname><![CDATA[testdb]]></dbname>
                    <initStatements><![CDATA[SET NAMES utf8]]></initStatements>
                    <model><![CDATA[mysql4]]></model>
                    <type><![CDATA[pdo_mysql]]></type>
                    <pdoType><![CDATA[]]></pdoType>
                    <active>1</active>
                </connection>
            </default_setup>
        </resources>
        <session_save><![CDATA[files]]></session_save>
    </global>
    <admin>
        <routers>
            <adminhtml>
                <args>
                    <frontName><![CDATA[admin]]></frontName>
                </args>
            </adminhtml>
        </routers>
    </admin>
</config>
'

# exec issue
#exec 3<&- 3<<<"$xmlstr"
#exec 3<&- 3< <(printf '%s' "$xmlstr")
exec 3<&- 3<<EOF
$(printf '%s' "$xmlstr")
EOF

{ read -r host; read -r username; read -r password; read -r dbname; } < <(
       echo "cat /config/global/resources/default_setup/connection/*[self::host or self::username or self::password or self::dbname]/text()" | 
          xmllint --nocdata --shell /dev/fd/3 | 
          sed -e '1d;$d' -e '/^ *--* *$/d'
       )

printf '%s\n' "host: $host" "username: $username" "password: $password" "dbname: $dbname"

exec 3<&-
}
set -H


# output
# host: localhost
# username: root
# password: pass123
# dbname: testdb


6

Obwohl es bereits viele Antworten gibt, stimme ich zu xml2.

$ xml2 < test.xml
/config/global/install/date=Tue, 11 Dec 2012 12:31:25 +0000
/config/global/crypt/key=70e75d7969b900b696785f2f81ecb430
/config/global/disable_local_modules=false
/config/global/resources/db/table_prefix
/config/global/resources/default_setup/connection/host=localhost
/config/global/resources/default_setup/connection/username=root
/config/global/resources/default_setup/connection/password=pass123
/config/global/resources/default_setup/connection/dbname=testdb
/config/global/resources/default_setup/connection/initStatements=SET NAMES utf8
/config/global/resources/default_setup/connection/model=mysql4
/config/global/resources/default_setup/connection/type=pdo_mysql
/config/global/resources/default_setup/connection/pdoType
/config/global/resources/default_setup/connection/active=1
/config/global/session_save=files
/config/admin/routers/adminhtml/args/frontName=admin

Mit ein wenig Magie können Sie diese sogar direkt als Variablen setzen:

$ eval $(xml2 < test.xml | tr '/, ' '___' | grep =)
$ echo $_config_global_resources_default_setup_connection_host          
localhost

3

Folgendes funktioniert, wenn Sie mit Ihren Testdaten arbeiten:

{ read -r host; read -r username; read -r password; read -r dbname; } \
  < <(xmlstarlet sel -t -m /config/global/resources/default_setup/connection \
      -v ./host -n \
      -v ./username -n \
      -v ./password -n \
      -v ./dbname -n)

Dies stellt den Inhalt in Variablen host, username, passwordund dbname.


xmlstarlet: Befehl nicht gefunden, daher ist dieser Befehl für mich nicht nützlich :(
MagePsycho

@MagePsycho bashunterstützt kein XML-Parsing. Sie benötigen entweder ein entsprechendes Tool (xmlstarlet, xsltproc, ein modernes Python usw.) oder Sie können XML nicht korrekt analysieren.
Charles Duffy

@CharlesDuffy gibt es eine Möglichkeit, den Wert mithilfe von Regex-Muster oder sonst zu erhalten?
MagePsycho

5
@MagePsycho Du kannst einfach xmlstarlet installieren. In jedem Fall sollten Sie niemals reguläre Ausdrücke verwenden, um (X) HTML zu analysieren .
Terdon

1
@MagePsycho Ich wollte den gleichen Link posten, den terdon bereits gepostet hat. Kurz gesagt: Nein
Charles Duffy

3

Eine reine bashFunktion, nur für den unglücklichen Fall, dass Sie nichts Passendes installieren dürfen. Dies kann und wird wahrscheinlich bei komplexerem XML fehlschlagen:

function xmlpath()
{
  local expr="${1//\// }"
  local path=()
  local chunk tag data

  while IFS='' read -r -d '<' chunk; do
    IFS='>' read -r tag data <<< "$chunk"

    case "$tag" in
      '?'*) ;;
      '!–-'*) ;;
      '![CDATA['*) data="${tag:8:${#tag}-10}" ;;
      ?*'/') ;;
      '/'?*) unset path[${#path[@]}-1] ;;
      ?*) path+=("$tag") ;;
    esac

    [[ "${path[@]}" == "$expr" ]] && echo "$data"
  done
}

Verwendung:

bash-4.1$ xmlpath 'config/global/resources/default_setup/connection/host' < MagePsycho.xml
localhost

Bekannte Probleme:

  • schleppend
  • sucht nur nach Tag-Namen
  • Keine Zeichenentitätsdecodierung

2

Mit xmllint und der Option --xpath ist dies sehr einfach. Sie können dies einfach tun:

XML_FILE=/path/to/file.xml

HOST=$(xmllint --xpath 'string(/config/global/resources/default_setup/connection/host)' $XML_FILE
USERNAME=$(xmllint --xpath 'string(/config/global/resources/default_setup/connection/username)' $XML_FILE
PASSWORD=$(xmllint --xpath 'string(/config/global/resources/default_setup/connection/password)' $XML_FILE 
DBNAME=$(xmllint --xpath 'string(/config/global/resources/default_setup/connection/dbname)' $XML_FILE

Wenn Sie zum Attribut eines Elements gelangen müssen, ist dies auch mit XPath ganz einfach. Stellen Sie sich vor, Sie haben die Datei:

<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<addon id="screensaver.turnoff"
       name="Turn Off"
       version="0.10.0"
       provider-name="Dag Wieërs">
  ..snip..
</addon>

Die benötigten Shell-Anweisungen wären:

VERSION=$(xmllint --xpath 'string(/addon/@version)' $ADDON_XML)
AUTHOR=$(xmllint --xpath 'string(/addon/@provider-name)' $ADDON_XML)

0

Sie können die PHP-Befehlszeilenschnittstellen-Codierung in Bash-Skripten verwenden, um mehrere komplexe Skripten zu verarbeiten, die sich tatsächlich über mehrere Codierungszeilen erstrecken. Versuchen Sie zunächst, Ihre Lösung mit PHP-Skripten zu erstellen, und übergeben Sie die Parameter später im CLI-Modus. Auf diese Weise können Sie die hervorragende Verwendung von XML-Parsern steuern.

Die Umgebung scheint, dass Sie PHP im Client-Modus über ssh / shell-Zugriff verwenden können.

php -f yourxmlparser.php

Jetzt mache alle Dinge in deiner PHP-Datei. Verwenden Sie die verfügbaren Befehlszeilenparameter.

Sie können diese Rückgabewerte sogar der Shell-Umgebung zuweisen, um den Rest Ihrer Shell-Skripte fortzusetzen.

Die andere Möglichkeit besteht darin, die Option | grep zu verwenden, um den erforderlichen Wert in der XML-Datei zu ermitteln, wenn Sie sich ziemlich sicher sind, dass sich die Struktur Ihrer XML-Datei im Laufe der Zeit nicht ändert.


0

Dieser Kommentar verwendet nur sh / bash Befehle und Methoden! /test.xml ist Ihre XML-Typ-Datei bei der ersten Frage ...

#!/bin/sh

cat /test.xml | while read line;do
[ "$(echo "$line" | grep "<host>")" ]&& echo "host: $(echo $line |  cut -f3 -d'[' | cut -f1 -d']')"
[ "$(echo "$line" | grep "<username>")" ]&& echo "username: $(echo $line |  cut -f3 -d'[' | cut -f1 -d']')"
[ "$(echo "$line" | grep "<password>")" ]&& echo "password: $(echo $line |  cut -f3 -d'[' | cut -f1 -d']')"
[ "$(echo "$line" | grep "<dbname")" ]&& echo "dbname: $(echo $line |  cut -f3 -d'[' | cut -f1 -d']')"
done

Ausgabe:

host: localhost
username: root
password: pass123
dbname: testdb

Wenn Sie diese Werte in eine Datei schreiben möchten, gehen Sie wie folgt vor:

#!/bin/sh

cat /test.xml | while read line;do
[ "$(echo "$line" | grep "<host>")" ]&& echo "$line" |  cut -f3 -d'[' | cut -f1 -d']' > /config/global/resources/default_setup/connection/host
[ "$(echo "$line" | grep "<username>")" ]&& echo "$line" |  cut -f3 -d'[' | cut -f1 -d']' > /config/global/resources/default_setup/connection/username
[ "$(echo "$line" | grep "<password>")" ]&& echo "$line" |  cut -f3 -d'[' | cut -f1 -d']' > /config/global/resources/default_setup/connection/password
[ "$(echo "$line" | grep "<dbname")" ]&& echo "$line" |  cut -f3 -d'[' | cut -f1 -d']' > /config/global/resources/default_setup/connection/dbname
done

Diese Methode überschreibt Ihre lokalen Dateien, wobei nur Werte abgerufen werden (Ihre Daten gehen aus den Ausgabedateien verloren).

Durch die Nutzung unserer Website bestätigen Sie, dass Sie unsere Cookie-Richtlinie und Datenschutzrichtlinie gelesen und verstanden haben.
Licensed under cc by-sa 3.0 with attribution required.