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, password와 dbname.


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.