栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > 其他 > xml语言

如何使用bash解析xml的示例代码分析

xml语言 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如何使用bash解析xml的示例代码分析

最初的需求是希望bash能提供完整成熟的xml解析工具来解析xml,但是并没有找到这样的工具。后来在StackOverFlow上找到一个简单的处理xml的方法,即:

rdom () { local IFS=> ; read -d < E C ;}

方法只有一行!(当然,两条语句应该算是两行……)

当然,这也只能处理最简单原始的xml,不能处理带属性的,不能有注释等等。

由于楼主过于懒惰,不想引入(学习)新的脚本语言,所以打算改造上面的方法。

改造之前,先来解释一下上面那行语句的意义。

其实很简单,这行命令的作用就是读取<与下一个<之间的字符

(xml中,如果在节点本身之外存在<或者>,属性值含有空格,则函数失效,所以我们假设xml中没有此情况)

有了上面的假设,那么两个<字符直接,就一定会有一个>字符,>将read读取的内容分为两部分,分别记做E和C,举个简单的例子:

value

第一次执行rdom时,read读取到<即结束了,所以E和C都是空字符串。

第二次执行rdom时,read读取到的内容为:tag>value,然后是<字符,read结束。所以E=tag;C=value

第三次执行rdom时,read读取到的内容为:/tag>到下一个<或文件末尾。所以E=/tag,C为空白符。

所以这种方式并不实用,我们想支持带属性的节点,我们也不想删除xml中的注释,我们甚至还想解析xml的声明,我们……好了,我们想的太多了。我们还是看看能做些什么吧。

我们可以看出,<>里面的部分是作为整体赋值给E的,那么解析属性就要对E做手脚。

(我们假设xml中,在节点本身之外存在没有<和>,属性值中也没有空格)

下面我们来操作一下,首先先引入一个输入空格,用来显示层级的函数echo_tabs

echo_tabs() {    local tabs="";    for((i = 0; i < $1; i++)); do        tabs=$tabs'    ' #4个空格    done    echo -n "$tabs" #一定要加双引号}

然后我们来解析xml中的声明,就是下面这部分

声明与其他标签闭合方式不同,并且尖括号内两端是?,所以这里要把它与普通节点区分。

read_dom() {    #备份IFS    local oldIFS=$IFS    local IFS=> #字段分割符改为>    read -d < ENTITY ConTENT #read分隔符改为<    local ret=$?    local ELEMENT=''    #第一次执行时,第一个字符为<.    #所以read执行完毕,ENTITY和CONTENT都是空白符    if [[ $ENTITY =~ ^[[:space:]]*$ ]] && [[ $ConTENT =~ ^[[:space:]]*$ ]]; then        return $ret    fi    # ENTITY = ?xml version="1.0" encoding="utf-8"?    #解析xml声明,并非普通节点,闭合方式与节点不同    if [[ "$ENTITY" =~ ^?xml[[:space:]]*(.*)?$ ]]; then #使用正则去除问号和xml字符        ENTITY=''        ELEMENT='' #不是普通节点        ATTRIBUTES="${BASH_REMATCH[1]}" #获取声明中的属性    else #普通节点        ELEMENT=${ENTITY%% *} #获取节点名称,如果ENTITY中有空格,则第一个空格前面部分即为节点名称        ATTRIBUTES=${ENTITY#* } #获取节点所有属性,如果ENTITY中有空格,则第一个空格后面部分为所有属性(#2和#4,#4情况下,会多出/)    fi}

下面我们来解析注释。注释让人烦恼的地方是,注释内可以包含尖括号!这里只做最简单处理,只解析不含尖括号的注释!

if [[ "$ENTITY" = !--*-- ]]; then #不检查注释    return 0fi

现在我们看xml中最关键的部分

我们知道,CONTENT为节点的内容,显示出来就可以了

if [[ ! "$CONTENT" =~ ^[[:space:]]*$ ]]; then    echo -n ConTENT=$CONTENTfi

节点自身属性都在ENTITY中,所以我们需要将节点名称与属性分开,然后再提取属性名和属性值

我们分别处理下面几种形式的节点

abc

我们之前已经将节点名称与属性分开了

ELEMENT=${ENTITY%% *} #获取节点名称,如果ENTITY中有空格,则第一个空格前面部分即为节点名称ATTRIBUTES=${ENTITY#* } #获取节点所有属性,如果ENTITY中有空格,则第一个空格后面部分为所有属性(#2和#4,#4情况下,会多出/)

但是上面的ATTRIBUTES变量会有个小问题,稍后说明

ELEMENT如果以/开头,那么这是读取到节点的闭合标签了

ELEMENT如果以/结尾,那么这是一个空标签,类似

其他情况ELEMENT均为节点名称,但是读取这类标签时,ELEMENT没有问题,ATTRIBUTES是以/结尾,也就是说,这时,标签已经闭合,并且我们需要将/从ATTRIBUTES末尾删除

#!/usr/bin/env bash#只适合解析简单xml,若属性值带有空格,注释中含有尖括号等,则无法解析#下面情况可以正常解析#0.#1.only For Test#2.#3.#4.#Attribute=Attribute Name#VALUE=Attribute Value#ELEMENT=Element Name#ConTENT=Element Content#接受一个int层级参数,层级从0开始echo_tabs() {    local tabs="";    for((i = 0; i < $1; i++)); do        tabs=$tabs'    ' #4个空格    done    echo -n "$tabs" #一定要加双引号}read_dom() {    #备份IFS    local oldIFS=$IFS    local IFS=> #字段分割符改为>    read -d < ENTITY ConTENT #read分隔符改为<    local ret=$?    local ELEMENT=''    #第一次执行时,第一个字符为<.    #所以read执行完毕,ENTITY和CONTENT都是空白符    if [[ $ENTITY =~ ^[[:space:]]*$ ]] && [[ $ConTENT =~ ^[[:space:]]*$ ]]; then        return $ret    fi    #第二次执行时,分为下面集中情况    #0.    #此时read结果为?xml version="1.0" encoding="utf-8"?    #ConTENT=若干空白符    #1.1785    #此时read结果为Size,所以ENTITY=Size,ConTENT='1785'    #第三次read结为/Size,所以ENTITY=/Size,ConTENT=若干空白符    #2.    #此时read结果为ListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/",    所以ENTITY=tListBucketResult xmlns="http://s3.amazonaws.com/doc/2006-03-01/",ConTENT=同#1    #3.    #此时read结果为test/,所以ENTITY=test/,ConTENT=若干空白符    #4.    #此时read结果为test name="xyz" age="21"/,所以ENTITY=test name="xyz"/,ConTENT=若干空白符    #5.    #此时read结果为!--q1--,所以ENTITY=!--q1--,ConTENT=''    # ENTITY = ?xml version="1.0" encoding="utf-8"?    #解析xml声明,并非普通节点,闭合方式与节点不同    if [[ "$ENTITY" =~ ^?xml[[:space:]]*(.*)?$ ]]; then #使用正则去除问号和xml字符        ENTITY=''        ELEMENT='' #不是普通节点        ATTRIBUTES="${BASH_REMATCH[1]}" #获取声明中的属性    else #普通节点        ELEMENT=${ENTITY%% *} #获取节点名称,如果ENTITY中有空格,则第一个空格前面部分即为节点名称        ATTRIBUTES=${ENTITY#* } #获取节点所有属性,如果ENTITY中有空格,则第一个空格后面部分为所有属性(#2和#4,#4情况下,会多出/)    fi    if [[ "$ENTITY" = !--*-- ]]; then #不检查注释(#5)        return 0    fi    if [[ "$ELEMENT" = } #删除/        return 0    elif [[ "$ELEMENT" = */  ]] || [[ $ATTRIBUTES = */  ]]; then #3或#4        empty=true #节点没有子节点,也没有value(自身为闭合标签)        if [[ $ATTRIBUTES = */  ]]; then #如果是#4情况            ATTRIBUTES=${ATTRIBUTES%*/} #将末尾的/删除,提取所有属性        fi        echo_tabs $tabCount        echo -n ELEMENT=${ELEMENT%*/}' '    elif [ ! "$ELEMENT" = '' ]; then #第一次执行时,ENTITY和CONTENT都是空串        echo_tabs $tabCount        echo -n ELEMENT="$ELEMENT"' ' #输出节点名        tabCount=$[$tabCount + 1] #新节点    else        echo -n "XML declaration " #ELEMENT为空,不计算层级    fi    local empty=false #没有子节点,没有value    IFS=$oldIFS #属性之间由空白符分割,恢复IFS,IFS默认为空格/换行/制表符    local hasAttribute=false #节点是否有属性    for a in $ATTRIBUTES; do #循环所有属性        #echo ATTRIBUTES=$ATTRIBUTES '   -+-+-+-   '        if [[ "$a" = *=* ]] #情况#2和#4        then            hasAttribute=true            ATTRIBUTE_NAME=${a%%=*} #提取属性名            ATTRIBUTE_VALUE=`tr -d '"' <<< ${a#*=}` #提取属性值并去掉双引号            echo -n ATTRIBUTE=$ATTRIBUTE_NAME VALUE=$ATTRIBUTE_VALUE' ' #输出属性名/属性值        fi    done    if [[ ! "$CONTENT" =~ ^[[:space:]]*$ ]]; then        echo -n ConTENT=$ConTENT    fi    if [ "$empty" = true ]; then        echo        echo_tabs $tabCount        echo -n END ${ELEMENT%/*} #删除/#        echo -n ' (empty node)'    fi    echo    return $ret}read_xml() {    local tabCount=0 #用来格式化输出,计算节点层级    while read_dom; do        :    done < test.xml}read_xml

对下面xml执行此脚本

                                only For Test                abc                            

输出结果为

以上就是如何使用bash解析xml的示例代码分析的详细内容,更多请关注考高分网其它相关文章!

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/794427.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号