Bash betiğinde düğüm değeri almak için XML ayrıştırılsın mı?


19

Aşağıdaki yollarla bir düğümün değerini nasıl alabilirim bilmek istiyorum:

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

aşağıdaki XML'den:

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

Ayrıca daha fazla kullanım için bu değeri değişkene atamak istiyorum. Fikrinizi bildirin.


7
Keyfi verilerin yapılandırılmış ağaçlarını ayrıştırmak için bash'ı asla kullanmayın. Gerçek bir XML ayrıştırıcı kullanın. Ben tavsiye XMLStarlet .
Chris Down

Yanıtlar:


19

bashVe xmllintetiketlerini kullanarak :

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

Yalnızca bir XML dizesi varsa ve geçici bir dosyanın kullanımından kaçınılması durumunda, dosya tanımlayıcıları xmllint(bu /dev/fd/3dosya argümanı olarak verilir ):

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

Halihazırda birçok cevap olmasına rağmen, ben de buna gireceğim 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

Biraz sihirle bunları doğrudan değişken olarak bile ayarlayabilirsiniz:

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

3

Test verilerinizle çalıştırıldığında aşağıdakiler çalışır:

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

Bu değişkenlere içeriği koyar host, username, passwordve dbname.


xmlstarlet: komut bulunamadı, bu yüzden bu komut benim için yararlı değil :(
MagePsycho

@MagePsycho bash, XML ayrıştırma için yerleşik bir desteğe sahip değildir. Ya (xmlstarlet, xsltproc, modern bir Python, vb.) Yapan bir araca sahip olmanız ya da XML'yi doğru ayrıştıramazsınız.
Charles Duffy

@CharlesDuffy regex desen veya başka bir değer kullanarak olabilir almak için bir yolu var mı?
MagePsycho

5
@MagePsycho xmlstarlet kurabilirsiniz. Her durumda, HTML'yi ayrıştırmak için asla normal ifadeler kullanmamalısınız .
terdon

1
@MagePsycho Terdon'un zaten yaptığı aynı bağlantıyı göndermek üzereydim. Kısacası: Hayır.
Charles Duffy

3

Saf bir bashişlev, sadece uygun bir şey yüklemenize izin verilmediğinde talihsiz durum için. Bu, daha karmaşık XML'de başarısız olabilir ve muhtemelen başarısız olacaktır:

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
}

Kullanımı:

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

Bilinen Sorunlar:

  • yavaş
  • yalnızca etiket adlarına göre arar
  • karakter varlığı kod çözme yok

2

Kullanılması xmllint komutunu ve --xpath seçeneği, bu çok kolaydır. Bunu basitçe yapabilirsiniz:

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

Bir öğenin özelliğine erişmeniz gerekiyorsa, XPath'i kullanmak da kolaydır. Dosyanın olduğunu düşünün:

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

Gerekli kabuk ifadeleri:

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

0

Aslında, birden fazla kodlama satırına yayılmış birkaç karmaşık komut dosyasını işlemek için bash komut dosyalarında php komut satırı arabirimi kodlamasını kullanabilirsiniz. İlk olarak, PHP komut dosyalarını kullanarak çözümünüzü yapmaya çalışın ve daha sonra CLI modunu kullanarak parametreleri geçirin. Böylece, XML ayrıştırıcılarının mükemmel kullanımları üzerinde kontrol sahibi olabilirsiniz.

Ortam, PHP'yi ssh / shell erişimi aracılığıyla istemci modunda kullanabileceğiniz gibi görünüyor.

php -f yourxmlparser.php

Şimdi, php dosyanızdaki her şeyi yapın. Alabileceği komut satırı parametrelerini kullanın.

Kabuk komut dosyalarınızın geri kalanına devam etmek için bu dönüş değerlerini Kabuk ortamına da atayabilirsiniz.

Diğer bir yol ise, xml dosyanızın zaman içinde değişmeyen yapısından eminseniz, xml dosyası içinde gerekli değerinizi eşleştirmek için | grep seçeneğini kullanmaktır.


0

Bu yorum yalnızca sh / bash komutlarını ve yöntemlerini kullanır! /test.xml ilk sorudaki XML türü dosyanızdır ...

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

çıktı:

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

u bu değerleri dosyaya yazmak istiyorsanız bu yöntemi kullanın:

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

bu yöntem yalnızca değer almak için kullanılan yerel dosyalarınızın üzerine yazacaktır (verileriniz çıktı dosyalarından kaybolacaktır)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.