Parse XML untuk mendapatkan nilai simpul dalam skrip bash?


19

Saya ingin tahu bagaimana saya bisa mendapatkan nilai simpul dengan jalur berikut:

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

dari XML berikut:

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

Saya juga ingin menetapkan nilai itu ke variabel untuk digunakan lebih lanjut. Biarkan saya tahu ide Anda.


7
Jangan pernah menggunakan bash untuk mem-parsing pohon terstruktur dari data sewenang-wenang. Gunakan parser XML nyata. Saya merekomendasikan XMLStarlet .
Chris Down

Jawaban:


19

Menggunakan bashdan xmllint(seperti yang diberikan oleh tag):

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

Jika hanya ada string XML dan penggunaan file sementara harus dihindari, deskriptor file adalah cara yang harus digunakan xmllint(yang diberikan /dev/fd/3sebagai argumen file di sini):

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

1
Halaman manual
Jason Pyeron

6

Meskipun sudah ada banyak jawaban, saya akan berbincang dengan 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

Dengan sedikit sihir, Anda bahkan dapat mengaturnya sebagai variabel secara langsung:

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

3

Hal berikut ini berfungsi saat dijalankan terhadap data pengujian Anda:

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

Hal ini menempatkan konten ke dalam variabel host, username, passworddan dbname.


xmlstarlet: perintah tidak ditemukan, jadi perintah ini tidak berguna bagi saya :(
MagePsycho

@MagePsycho bashtidak memiliki dukungan bawaan untuk penguraian XML. Anda harus memiliki alat yang berfungsi (xmlstarlet, xsltproc, Python modern, dll), atau Anda tidak dapat menguraikan XML dengan benar.
Charles Duffy

@CharlesDuffy apakah ada cara untuk mendapatkan nilai mungkin menggunakan pola regex atau yang lain?
MagePsycho

5
@MagePsycho Anda cukup menginstal xmlstarlet. Dalam hal apa pun, Anda tidak boleh menggunakan ekspresi reguler untuk mem-parsing (X) HTML .
terdon

1
@MagePsycho saya akan memposting tautan terdon yang sama dengan yang sudah saya lakukan. Singkatnya: Tidak.
Charles Duffy

3

bashFungsi murni , hanya untuk kasus malang ketika Anda tidak diizinkan untuk menginstal apa pun yang sesuai. Ini mungkin, dan mungkin akan, gagal pada XML yang lebih rumit:

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
}

Pemakaian:

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

Masalah Dikenal:

  • lambat
  • hanya mencari berdasarkan nama tag
  • tidak ada decoding entitas karakter

2

Menggunakan xmllint dan opsi --xpath , sangat mudah. Anda cukup melakukan ini:

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

Jika Anda perlu mendapatkan atribut elemen, itu juga mudah menggunakan XPath. Bayangkan Anda memiliki file:

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

Pernyataan shell yang dibutuhkan adalah:

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

0

Anda dapat menggunakan pengkodean antarmuka baris perintah php dalam skrip bash untuk menangani beberapa skrip kompleks yang sebenarnya menjangkau beberapa baris pengkodean. Pertama, coba buat solusi Anda menggunakan skrip PHP, dan kemudian berikan parameter menggunakan mode CLI. Dengan demikian, Anda bisa mendapatkan kontrol atas penggunaan parser XML yang luar biasa.

Lingkungan sepertinya Anda dapat menggunakan PHP dalam mode klien melalui akses ssh / shell.

php -f yourxmlparser.php

Sekarang, lakukan semua hal dalam file php Anda. Manfaatkan parameter baris perintah yang dapat diambil.

Anda bahkan dapat menetapkan bahwa nilai pengembalian ke lingkungan Shell untuk melanjutkan sisa skrip shell Anda.

Dan cara lain adalah dengan menggunakan opsi | grep untuk mencocokkan nilai yang Anda butuhkan dalam file xml, jika Anda cukup yakin dengan struktur file xml Anda yang tidak berubah seiring waktu.


0

Komentar ini hanya menggunakan perintah dan metode sh / bash! /test.xml adalah file jenis XML Anda di pertanyaan pertama ...

#!/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

keluaran:

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

jika Anda ingin menulis nilai ini ke file, gunakan metode ini:

#!/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

metode ini akan menimpa file lokal Anda hanya menggunakan nilai (data Anda akan hilang dari file output)

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.