แยกวิเคราะห์ XML เพื่อรับค่าโหนดในสคริปต์ทุบตี?


19

ฉันต้องการทราบวิธีการรับค่าของโหนดด้วยพา ธ ต่อไปนี้:

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

จาก 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>

นอกจากนี้ฉันต้องการกำหนดค่าให้กับตัวแปรสำหรับการใช้งานเพิ่มเติม แจ้งให้เราทราบความคิดของคุณ


7
อย่าใช้ bash เพื่อแยกวิเคราะห์แผนผังที่มีโครงสร้างของข้อมูลใด ๆ ใช้ตัวแยกวิเคราะห์ XML จริง ผมขอแนะนำให้XMLStarlet
Chris Down

คำตอบ:


19

การใช้bashและxmllint(ตามที่กำหนดโดยแท็ก):

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

ในกรณีที่มีเพียงสตริง XML และควรหลีกเลี่ยงการใช้ไฟล์ชั่วคราวตัวอธิบายไฟล์เป็นวิธีที่จะดำเนินการxmllint(ซึ่งได้รับ/dev/fd/3เป็นอาร์กิวเมนต์ไฟล์ที่นี่):

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
หน้าคน: xmlsoft.org/xmllint.html
Jason Pyeron

6

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

ด้วยเวทย์มนตร์เล็ก ๆ คุณสามารถตั้งเป็นตัวแปรได้โดยตรง:

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

3

การทำงานต่อไปนี้เมื่อทำงานกับข้อมูลทดสอบของคุณ:

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

ซึ่งจะทำให้เนื้อหาลงในตัวแปรhost, username, และpassworddbname


xmlstarlet: ไม่พบคำสั่งดังนั้นคำสั่งนี้ไม่มีประโยชน์กับฉัน :(
MagePsycho

@MagePsycho bashไม่รองรับการแยกวิเคราะห์ XML ในตัว คุณต้องมีเครื่องมือที่ทำ (xmlstarlet, xsltproc, Python ที่ทันสมัย ​​ฯลฯ ) หรือคุณไม่สามารถแยกวิเคราะห์ XML ได้อย่างถูกต้อง
Charles Duffy

@CharlesDuffy มีวิธีรับค่าอาจใช้รูปแบบ regex หรือไม่?
MagePsycho

5
@MagePsycho คุณสามารถติดตั้ง xmlstarlet ในกรณีใด ๆ ที่คุณควรจะไม่ใช้การแสดงออกปกติจะแยก (X)
terdon

1
@MagePsycho ฉันกำลังจะโพสต์ลิงก์เดียวกัน terdon แล้ว ในระยะสั้น: ไม่
ชาร์ลส์ดัฟฟี่

3

bashฟังก์ชันบริสุทธิ์สำหรับกรณีที่โชคร้ายเมื่อคุณไม่ได้รับอนุญาตให้ติดตั้งสิ่งที่เหมาะสม สิ่งนี้อาจและอาจจะล้มเหลวบน XML ที่ซับซ้อนมากขึ้น:

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
}

การใช้งาน:

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

ปัญหาที่ทราบ:

  • ช้า
  • ค้นหาด้วยชื่อแท็กเท่านั้น
  • ไม่มีการถอดรหัสเอนทิตีอักขระ

2

การใช้xmllintและตัวเลือก--xpathนั้นง่ายมาก คุณสามารถทำได้:

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

หากคุณต้องการเข้าถึงองค์ประกอบขององค์ประกอบนั่นก็เป็นเรื่องง่ายที่จะใช้ XPath ลองนึกภาพคุณมีไฟล์:

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

คำสั่งเชลล์ที่ต้องการคือ:

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

0

คุณสามารถใช้ประโยชน์จากการเข้ารหัสอินเตอร์เฟสบรรทัดคำสั่ง php ในสคริปต์ทุบตีเพื่อจัดการสคริปต์ที่ซับซ้อนหลายตัวที่ครอบคลุมการเข้ารหัสหลายบรรทัด อันดับแรกลองสร้างโซลูชันของคุณโดยใช้สคริปต์ PHP จากนั้นจึงส่งต่อพารามิเตอร์โดยใช้โหมด CLI ดังนั้นคุณสามารถควบคุมการใช้ parsers XML ที่ยอดเยี่ยมได้

สภาพแวดล้อมดูเหมือนว่าคุณสามารถใช้ PHP ในโหมดไคลเอนต์ผ่านการเข้าถึง ssh / shell

php -f yourxmlparser.php

ตอนนี้ทำทุกสิ่งในไฟล์ php ของคุณ ใช้ประโยชน์จากพารามิเตอร์บรรทัดคำสั่งที่สามารถใช้ได้

คุณสามารถกำหนดค่าที่ส่งคืนให้กับสภาพแวดล้อมของเชลล์เพื่อดำเนินการต่อในส่วนที่เหลือของสคริปต์เชลล์ของคุณ

และอีกวิธีคือใช้ตัวเลือก | grep เพื่อจับคู่ค่าที่ต้องการของคุณภายในไฟล์ xml หากคุณมั่นใจในโครงสร้างของไฟล์ xml ที่ไม่เปลี่ยนแปลงตลอดเวลา


0

ความคิดเห็นนี้ใช้เฉพาะคำสั่งและวิธีการ sh / bash เท่านั้น! /test.xml เป็นไฟล์ประเภท XML ของคุณในคำถามแรก ...

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

เอาท์พุท:

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

ถ้าคุณต้องการเขียนค่านี้ไปยังไฟล์ใช้วิธีนี้:

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

วิธีนี้จะเขียนทับไฟล์ภายในเครื่องของคุณที่ใช้รับเฉพาะค่า (ข้อมูลของคุณจะหายไปจากไฟล์เอาต์พุต)

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.