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

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

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

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

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

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

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

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

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

<tag>value</tag>

第一次执行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++)); dotabs=$tabs'    ' #4个空格doneecho -n "$tabs" #一定要加双引号
}

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

<?xml version="1.0" encoding="utf-8"?>

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

read_dom() {#备份IFSlocal oldIFS=$IFSlocal IFS=\> #字段分割符改为>read -d \< ENTITY CONTENT #read分隔符改为<local ret=$?local ELEMENT=''#第一次执行时,第一个字符为<.#所以read执行完毕,ENTITY和CONTENT都是空白符if [[ $ENTITY =~ ^[[:space:]]*$ ]] && [[ $CONTENT =~ ^[[:space:]]*$ ]]; thenreturn $retfi# 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 0
fi

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

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

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

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

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

<test a="1"/>
<test></test>
<test>abc</test>
<test/>

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

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

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

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

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

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

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

对下面xml执行此脚本

<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2010 The Android Open Source ProjectLicensed under the Apache License, Version 2.0 (the "License");you may not use this file except in compliance with the License.You may obtain a copy of the License athttp://www.apache.org/licenses/LICENSE-2.0Unless required by applicable law or agreed to in writing, softwaredistributed under the License is distributed on an "AS IS" BASIS,WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.See the License for the specific language governing permissions andlimitations under the License.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"package="com.android.test"><uses-permission android:name="android.permission.BLUETOOTH" /><uses-permission android:name="android.permission.BLUETOOTH_ADMIN" /><uses-permission android:name="android.permission.BLUETOOTH_PRIVILEGED" /><application android:name=".TestApplication"android:icon="@drawable/icon"android:label="@string/app_name"><meta-data android:name="com.google.android.backup.api_key"android:value="AEdPqrEAAAAIbiKKs0wlimxeJ9y8iRIaBOH6aeb2IurmZyBHvg" /><test>Only For Test</test><test></test><test>abc</test><test/><activity android:name=".cardemulation.AppChooserActivity"android:finishOnCloseSystemDialogs="true"android:excludeFromRecents="true"/><service android:name=".handover.HandoverService"android:process=":handover"/></application>
</manifest>

输出结果为

转贴请保留以下链接

本人blog地址

http://su1216.iteye.com/

http://blog.csdn.net/su1216/

使用bash解析xml相关推荐

  1. Android解析xml的方法,Android中解析XML格式数据的方法

    XML介绍:Extensible Markup Language,即可扩展标记语言 一.概述 Android中解析XML格式数据大致有三种方法: SAX DOM PULL 二.详解 2.1 SAX S ...

  2. 用python解析xml的几种方法,Python_XML的三种解析方法

    什么是XML? XML 指可扩展标记语言(eXtensible Markup Language). XML 被设计用来传输和存储数据. XML是一套定义语义标记的规则,这些标记将文档分成许多部件并对这 ...

  3. python 使用sax 解析xml 文件

    这里不是说xml 的所以如果xml 不了解,可以百度大致看下即可, SAX知识了解 SAX (simple API for XML )  有解析器和事件处理器 解析器负责读取XML文档,并向事件处理器 ...

  4. c语言解析xml字符串_Python XML解析

    Python XML解析 什么是XML? XML 指可扩展标记语言(eXtensible Markup Language). 你可以通过本站学习XML教程 XML 被设计用来传输和存储数据. XML是 ...

  5. python中利用lxml模块解析xml文件报错XMLSyntaxError: Opening and ending tag mismatch

    今天在代码中第一次使用lxml解析xml文件时出错了, XMLSyntaxError: Opening and ending tag mismatch: keyEffectiveDate line 2 ...

  6. XML 解析XML文档 XML约束

    XML 什么是XML Extensible Markup Language(可扩展的标记语言) 他是一个语言,有自己的语法,和Java以及其他的编程无关 "标记" 在文件中包含类似 ...

  7. VC解析XML--使用CMarkup类解析XML

    经过今天尝试MFC解析XML串,也算有了不少收获,总结一下.          我是使用的CMarkup类对XML进行操作.                    CMarkup好象都是先从一个xml ...

  8. JAVA动态读取xml_Java动态生成和解析xml文件步骤详解

    packageday12;importjava.io.FileInputStream;importjava.util.ArrayList;importjava.util.List;importorg. ...

  9. java xml中的冒号_Java jdom解析xml文件带冒号的属性

    Java jdom解析xml文件带冒号的属性 如果xml文件解析带了冒号的属性,一般都是要特别处理,这里是命名空间,N年前遇到过一次忘记记录,后来也忘了,这次再记录下. 解决了,记录下,分享给大家,百 ...

最新文章

  1. buildConfigField 使用
  2. 深度|全面了解一下这个世界上最奇葩的公司:Google!
  3. MDT2010新功能(15)——完成部署后操作
  4. 阿里云容器Kubernetes监控(七) - Prometheus监控方案部署
  5. linux 有线网络管理,uos linux网络管理
  6. django批量修改table_django-formset实现数据表的批量操作
  7. 空中最亮的星用计算机弹数字,赤峰——夜空中最亮的星_数字展示在线
  8. avs v7.0 php,AVS v7.0 – PHP在线视频上传分享程序
  9. 【Java】国外大神总结的 10 个 Java 编程技巧!
  10. 服务器上flash不显示动画,解决Firefox上网看不了Flash网页的问题
  11. 为什么Java有GC调优而没听说过有CLR的GC调优?
  12. android延时摄影功能,学会这几招!让你玩转荣耀30夜景延时摄影
  13. JAX XLA 还没开始
  14. Linux中磁盘的管理(格式化、分区、挂载)
  15. C语言-快速回忆_float和double的输入输出格式
  16. P1875 佳佳的魔法药水
  17. IOS 将百度网盘中的文件直接发到微信而不是通过小程序或是网盘链接
  18. 狂神Spring Boot 员工管理系统 【源码 + 笔记 + web素材】 超详细整理
  19. 界面与程序分离---MIS开发新方法
  20. [CVPR2021 Oral] PREDATOR: Registration of 3D Point Clouds with Low Overlap 损失函数含代码理解

热门文章

  1. 利用Tsai-lenz算法实现手眼标定
  2. 牛客网利用C语言解兔子序列
  3. 2017滴滴校招 连续最大和(DP)
  4. 心电图心电轴怎么计算_心电图QRS电轴角度简易直读法
  5. Arduino通过串口透传ESP 13板与java程序交互
  6. 名人谈酒:李白、曹操、杜甫、金庸是这样评价酒的!
  7. eq, neq.gt,ge,lte,lt,not,mod的含义
  8. 4.分支语句和循环语句
  9. CIO谈:基于K2 BPM平台怎么做报销?
  10. 视频播放库Vitamio的使用以及功能扩展