热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

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

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

rdom () { local IFS=\> ; read -d \

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

当然,这也只能处理最简单原始的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&#39;    &#39; #4个空格
    done
    echo -n "$tabs" #一定要加双引号
}

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

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

read_dom() {
    #备份IFS
    local oldIFS=$IFS

    local IFS=\> #字段分割符改为>
    read -d \

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

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

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

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

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

节点自身属性都在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&#39;    &#39; #4个空格
    done
    echo -n "$tabs" #一定要加双引号
}

read_dom() {
    #备份IFS
    local oldIFS=$IFS

    local IFS=\> #字段分割符改为>
    read -d \
    #此时read结果为?xml version="1.0" encoding="utf-8"?
    #COnTENT=若干空白符

    #1.1785
    #此时read结果为Size,所以ENTITY=Size,COnTENT=&#39;1785&#39;
    #第三次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=&#39;&#39;

    # ENTITY = ?xml version="1.0" encoding="utf-8"?
    #解析xml声明,并非普通节点,闭合方式与节点不同
    if [[ "$ENTITY" =~ ^\?xml[[:space:]]*(.*)\?$ ]]; then #使用正则去除问号和xml字符
        ENTITY=&#39;&#39;
        ELEMENT=&#39;&#39; #不是普通节点
        ATTRIBUTES="${BASH_REMATCH[1]}" #获取声明中的属性
    else #普通节点
        ELEMENT=${ENTITY%% *} #获取节点名称,如果ENTITY中有空格,则第一个空格前面部分即为节点名称
        ATTRIBUTES=${ENTITY#* } #获取节点所有属性,如果ENTITY中有空格,则第一个空格后面部分为所有属性(#2和#4,#4情况下,会多出/)
    fi

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

    if [[ "$ELEMENT" = /* ]]; then #节点末尾 #1第三步
        tabCount=$[$tabCount - 1]
        echo_tabs $tabCount
        echo END ${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%*/}&#39; &#39;
    elif [ ! "$ELEMENT" = &#39;&#39; ]; then #第一次执行时,ENTITY和CONTENT都是空串
        echo_tabs $tabCount
        echo -n ELEMENT="$ELEMENT"&#39; &#39; #输出节点名
        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 &#39;   -+-+-+-   &#39;
        if [[ "$a" = *=* ]] #情况#2和#4
        then
            hasAttribute=true
            ATTRIBUTE_NAME=${a%%=*} #提取属性名
            ATTRIBUTE_VALUE=`tr -d &#39;"&#39; <<<${a#*=}` #提取属性值并去掉双引号
            echo -n ATTRIBUTE=$ATTRIBUTE_NAME VALUE=$ATTRIBUTE_VALUE&#39; &#39; #输出属性名/属性值
        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 &#39; (empty node)&#39;
    fi

    echo
    return $ret
}

read_xml() {
    local tabCount=0 #用来格式化输出,计算节点层级
    while read_dom; do
        :
    done 

对下面xml执行此脚本




    
    
    
    
        
        Only For Test
        
        abc
        

        
        
    

输出结果为

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


推荐阅读
  • 本文讨论了Alink回归预测的不完善问题,指出目前主要针对Python做案例,对其他语言支持不足。同时介绍了pom.xml文件的基本结构和使用方法,以及Maven的相关知识。最后,对Alink回归预测的未来发展提出了期待。 ... [详细]
  • 本文介绍了如何使用PHP向系统日历中添加事件的方法,通过使用PHP技术可以实现自动添加事件的功能,从而实现全局通知系统和迅速记录工具的自动化。同时还提到了系统exchange自带的日历具有同步感的特点,以及使用web技术实现自动添加事件的优势。 ... [详细]
  • 本文介绍了在开发Android新闻App时,搭建本地服务器的步骤。通过使用XAMPP软件,可以一键式搭建起开发环境,包括Apache、MySQL、PHP、PERL。在本地服务器上新建数据库和表,并设置相应的属性。最后,给出了创建new表的SQL语句。这个教程适合初学者参考。 ... [详细]
  • 在说Hibernate映射前,我们先来了解下对象关系映射ORM。ORM的实现思想就是将关系数据库中表的数据映射成对象,以对象的形式展现。这样开发人员就可以把对数据库的操作转化为对 ... [详细]
  • 本文介绍了在SpringBoot中集成thymeleaf前端模版的配置步骤,包括在application.properties配置文件中添加thymeleaf的配置信息,引入thymeleaf的jar包,以及创建PageController并添加index方法。 ... [详细]
  • 知识图谱——机器大脑中的知识库
    本文介绍了知识图谱在机器大脑中的应用,以及搜索引擎在知识图谱方面的发展。以谷歌知识图谱为例,说明了知识图谱的智能化特点。通过搜索引擎用户可以获取更加智能化的答案,如搜索关键词"Marie Curie",会得到居里夫人的详细信息以及与之相关的历史人物。知识图谱的出现引起了搜索引擎行业的变革,不仅美国的微软必应,中国的百度、搜狗等搜索引擎公司也纷纷推出了自己的知识图谱。 ... [详细]
  • Java验证码——kaptcha的使用配置及样式
    本文介绍了如何使用kaptcha库来实现Java验证码的配置和样式设置,包括pom.xml的依赖配置和web.xml中servlet的配置。 ... [详细]
  • Android中高级面试必知必会,积累总结
    本文介绍了Android中高级面试的必知必会内容,并总结了相关经验。文章指出,如今的Android市场对开发人员的要求更高,需要更专业的人才。同时,文章还给出了针对Android岗位的职责和要求,并提供了简历突出的建议。 ... [详细]
  • 本文讨论了如何优化解决hdu 1003 java题目的动态规划方法,通过分析加法规则和最大和的性质,提出了一种优化的思路。具体方法是,当从1加到n为负时,即sum(1,n)sum(n,s),可以继续加法计算。同时,还考虑了两种特殊情况:都是负数的情况和有0的情况。最后,通过使用Scanner类来获取输入数据。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • 本文介绍了OC学习笔记中的@property和@synthesize,包括属性的定义和合成的使用方法。通过示例代码详细讲解了@property和@synthesize的作用和用法。 ... [详细]
  • Mac OS 升级到11.2.2 Eclipse打不开了,报错Failed to create the Java Virtual Machine
    本文介绍了在Mac OS升级到11.2.2版本后,使用Eclipse打开时出现报错Failed to create the Java Virtual Machine的问题,并提供了解决方法。 ... [详细]
  • Android Studio Bumblebee | 2021.1.1(大黄蜂版本使用介绍)
    本文介绍了Android Studio Bumblebee | 2021.1.1(大黄蜂版本)的使用方法和相关知识,包括Gradle的介绍、设备管理器的配置、无线调试、新版本问题等内容。同时还提供了更新版本的下载地址和启动页面截图。 ... [详细]
  • 本文讲述了作者通过点火测试男友的性格和承受能力,以考验婚姻问题。作者故意不安慰男友并再次点火,观察他的反应。这个行为是善意的玩人,旨在了解男友的性格和避免婚姻问题。 ... [详细]
  • 1,关于死锁的理解死锁,我们可以简单的理解为是两个线程同时使用同一资源,两个线程又得不到相应的资源而造成永无相互等待的情况。 2,模拟死锁背景介绍:我们创建一个朋友 ... [详细]
author-avatar
jnto.
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有