最初的需求是希望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++)); do tabs=$tabs' ' #4个空格 done echo -n "$tabs" #一定要加双引号 }
然后我们来解析xml中的声明,就是下面这部分
<?xml version="1.0" encoding="utf-8"?>
声明与其他标签闭合方式不同,并且尖括号内两端是?,所以这里要把它与普通节点区分。
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 0 fi
现在我们看xml中最关键的部分
我们知道,CONTENT为节点的内容,显示出来就可以了
if [[ ! "$CONTENT" =~ ^[[:space:]]*$ ]]; then echo -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++)); 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.<?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情况下,会多出/) 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%*/}' ' 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 < "$1" } read_xml "$1"
对下面xml执行此脚本
<?xml version="1.0" encoding="utf-8"?> <!-- Copyright (C) 2010 The Android Open Source Project Licensed 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 at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed 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 and limitations 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地址
相关推荐
是一个小巧而简单的 bash 工具,用于在 build.xml ant 脚本和 manifest.xml 文件中修改模块版本。 在 nodejs 中制作。 它在工作目录中搜索 build.xml 或 manifest.xml 文件。 如果找到,它会解析 xml 以查找包含模块...
编辑您的~/.bash_profile以包含该行 export JAVA_HOME=/path/to/java8 ##基本用法 通过编辑 schema.properties 文件,您可以自定义解析器解释 xml 文件的方式。 运行该程序时,“xml”文件夹中的所有文件都将转换为...
产品特点 易于使用 反序列化XML到对象列表 将JSON反序列化为对象列表 计算解析时间(用于比较)屏幕截图主应用要求Java 8 Maven安装从下载部分下载存储库文件(项目),或通过在bash中键入以下命令来克隆此项目: ...
然而,像 XML 解析这样的东西太慢了,我不知道我是否能完成这些部分。 初始构建测试是在 Mac OS Lion 和 openSuSE 12.1 上完成的。 但是,目前的测试仅在 Mac OS X Yosemite、openSuSE 和 CentOS 上进行
Bash 脚本bulk_scan.sh读取文件urls.txt ; 对于urls.txt每一行(或 URL),运行urls.txt , whatweb结果记录到scan_output文件夹中的 json 格式文件中。 bulk_scan.sh完成后,可以运行 Python 脚本parse.py将...
Bash编程环境(BPE)是用于GNU Bash Shell的一组有用的Shell / CGI脚本功能。 每组功能都分类到一个单独的库中。 库包括哈希图,MySQL API,XML解析,CGI操作等。
- **Beautiful Soup:** 用于解析HTML和XML文档,方便提取需要的信息。 - **Requests:** 用于发送HTTP请求,获取网页的原始数据。 - **Scrapy:** 一个高层次的Web爬虫框架,可以更方便地构建和部署爬虫。 ### 2. ...
ipapi.co ipapi是使用python语言使用的,旨在通过此工具查找或跟踪IP ipapi使用来自api,并使用xml解析创建这些工具中混合了RegEx 并且已经过测试,如果有错误,请联系使用python3的工具如何安装[Termux] 1....
抓取MIT-OCW 一些用于抓取 MIT OpenCourseWare 课程大纲的脚本。用法bash 脚本main.sh将下载 MIT OpenCourseWare 并将 HTML 解析为 XML。 结果将转储到标准输出。作者茶玉洁, 刘汉晓
因此,使用C ++实现的后端可实现高性能,与此同时,为简化起见,可以从python使用所有打包的软件包。 运行项目: cd docker docker-compose build docker-compose run clang bash ./src/core/cmd/build.sh ...
如果每首歌曲都用数字索引,您可以使用 CMD 或 Bash 命令轻松完成此操作,但是如果您使用 iTunes,例如您的文件不会根据它们在播放列表中的位置用索引命名 :( 目前,这个 Java 程序将在导出播放列表时采用 iTunes ...
pov.xml 文件中保持更新。 为了完整起见,以下是当前使用的库列表: (编写单元测试的Java框架) (Java 序列化库,可以将 Java 对象转换为 JSON 并返回) (Java HTML 解析器库) 构建和运行步骤 首先,克隆这个...
当调用某些基于终端的程序时(例如Emacs,vi),可以禁用捕获输出将数据记录在XML文件中,以方便其他应用程序进行解析接触有关ShellLogger的问题,请联系Lorin Hochstein: 。先决条件ShellLogger需要Python 2.1或...
bnf.lang hcl.lang mssql.lang qu.lang xml.lang boo.lang html.lang mxml.lang r.lang xpp.lang c.lang httpd.lang n3.lang rebol.lang yaiff.lang ceylon.lang icon.lang nasal.lang ...