方法只有一行!(当然,两条语句应该算是两行……)
当然,这也只能处理最简单原始的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社区 其它相关文章!