bash 스크립트에서 노드 값을 얻기 위해 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 bash는 XML 구문 분석을 기본적으로 지원하지 않습니다. XMLstarlet, xsltproc, 최신 Python 등을 지원하는 도구가 필요하거나 XML을 올바르게 구문 분석 할 수 없습니다.
찰스 더피

@CharlesDuffy 값을 얻는 방법이 정규식 패턴이나 다른 것을 사용하고 있습니까?
MagePsycho

5
@MagePsycho 당신은 xmlstarlet을 설치할 수 있습니다. 어쨌든 정규 표현식을 사용하여 (X) HTML을 구문 분석 해서는 안됩니다 .
terdon

1
@MagePsycho 나는 이미 같은 링크를 게시하려고했다. 간단히 말해서 : 아니오.
Charles Duffy

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

bash 스크립트에서 php 명령 행 인터페이스 코딩을 사용하여 실제로 여러 줄의 코딩에 걸쳐있는 여러 복잡한 스크립트를 처리 할 수 ​​있습니다. 먼저 PHP 스크립트를 사용하여 솔루션을 만든 다음 CLI 모드를 사용하여 매개 변수를 전달하십시오. 따라서 XML 파서의 뛰어난 사용법을 제어 할 수 있습니다.

환경은 ssh / shell 액세스를 통해 클라이언트 모드에서 PHP를 사용할 수있는 것으로 보입니다.

php -f yourxmlparser.php

이제 PHP 파일 내에서 모든 작업을 수행하십시오. 사용할 수있는 명령 행 매개 변수를 사용하십시오.

쉘 값을 쉘 환경에 할당하여 나머지 쉘 스크립트를 계속할 수도 있습니다.

그리고 다른 방법은 시간이 지나도 변경되지 않는 xml 파일의 구조가 확실한 경우, grep 옵션을 사용하여 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.