如何在Bash中解析XML?

理想情况下,我希望能够做到的是:

cat xhtml文件.xhtml|
getElementViaXPath--path='/html/head/title'|
sed-e的%(^<title>|</title>$)%g'>titleOfXHTMLPage.txt

这实际上只是对Yuzem答案的解释,但我觉得不应该对其他人做这么多编辑,评论不允许格式化,所以

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

让我们将其称为“read_dom”而不是“rdom”,将其隔开一点并使用更长的变量:

读取dom(){
本地IFS=\>
读取-d\<实体内容
}

它定义了一个名为read_dom的函数。第一行将IFS(输入字段分隔符)设置为该函数的本地,并将其更改为>。这意味着,当您读取数据而不是在空格、制表符或换行符上自动拆分时,它会在“>”上拆分。下一行表示从stdin读取输入,而不是在换行时停止,而是在看到“<”时停止字符(去离子器标志的-d)。然后使用IFS拆分读取的内容,并将其分配给变量实体和内容。因此,采取以下措施:

<标签>价值&lt/标签>

read\u dom的第一次调用获取一个空字符串(因为“<”是第一个字符)。因为没有’>’字符,所以IFS将其拆分为”>’。Read然后为这两个变量分配一个空字符串。第二个调用获取字符串“tag>value”。然后由IFS将其拆分为两个字段“tag”和“value”。Read然后分配变量,如:ENTITY=tagCONTENT=value。第三个调用获取字符串“/tag>”。被IFS拆分为两个字段“/tag”和“”。Read然后分配如下变量:ENTITY=/tagCONTENT=。第四个调用将返回非零状态,因为我们已到达文件末尾

现在,他的while循环进行了一些清理,以符合上述要求:

读取dom时

;做
如果[$ENTITY=“title”];然后
echo$内容
出口
fi
完成<xhtml文件.xhtml>titleOfXHTMLPage.txt

第一行只是说,“当read_dom函数返回零状态时,执行以下操作。”第二行检查我们刚才看到的实体是否为“title”。下一行回显标记的内容。四条线出口。如果不是标题实体,则循环在第六行重复。我们将“xhtmlfile.xhtml”重定向到标准输入(用于read\u dom函数),并将标准输出重定向到“titleOfXHTMLPage.txt”(循环中前面的回音)

现在为input.xml提供以下内容(类似于在S3上列出一个bucket):

<ListBucketResult xmlns=”http://s3.amazonaws.com/doc/2006-03-01/“>
<名称>sth项目&lt/名称>
<IsTruncated>假&lt/IsTruncated>
<内容>
<键>苹果[email protected]&lt/键>
<最后修改>2011-07-25T22:23:04.000Z&lt/最后修改>
<ETag&gt&引用;0032a28286680abee71aed5d059c6a09&引用&lt/ETag>
<尺寸>1785年&lt/尺寸>
<StorageClass>标准&lt/StorageClass>
&lt/内容>
&lt/ListBucketResult>

以及以下循环:

读取dom时

;做
echo“$ENTITY=>$CONTENT”
完成<input.xml

你应该得到:

=>
ListBucketResult xmlns=”http://s3.amazonaws.com/doc/2006-03-01/“=>
名称=>某物项目
/名称=>
IsTruncated=>错误的
/IsTruncated=>
内容=>
键=>苹果[email protected]
/键=>
LastModified=>2011-07-25T22:23:04.000Z
/LastModified=>
ETag=&gt&引用;0032a28286680abee71aed5d059c6a09&引用;
/ETag=>
大小=>1785
/大小=>
StorageClass=>标准
/StorageClass=>
/内容=>

因此,如果我们编写了一个while循环,如Yuzem的循环:

读取dom时

;做
如果[$ENTITY=“Key”];然后
echo$内容
fi
完成<input.xml

我们将得到S3存储桶中所有文件的列表

编辑
如果出于某种原因本地IFS=\&gt不适用于您,如果您对其进行全局设置,则应在函数末尾重置它,如:

读取dom(){
原始_IFS=$IFS
IFS=\>
读取-d\<实体内容
IFS=$ORIGINAL_IFS
}

否则,您稍后在脚本中执行的任何行分割都将出错

编辑2
要拆分属性名称/值对,可以扩充read\u dom()如下:

读取dom(){
本地IFS=\>
读取-d\<实体内容
本地ret=$?
TAG_NAME=${ENTITY%%*}
属性=${ENTITY}
返回$ret
}

然后编写函数解析并获取所需数据,如下所示:

解析dom(){
如果[$TAG_NAME=“foo”];则
评估本地$ATTRIBUTES
echo“foo大小为:$size”
elif[$TAG_NAME=“bar”];然后
评估本地$ATTRIBUTES
echo“条形图类型为:$type”
fi
}

然后,当您读取dom时,调用解析dom

读取dom时

;做
解析dom
完成

然后给出以下标记示例:

<示例>
<棒材尺寸=“棒材尺寸”类型=“金属”>钢筋含量&lt/酒吧>
<foo size=“1789”type=“未知”>食品内容&lt/foo>
&lt/示例>

您应该获得以下输出:

$cat example.xml |/bash_xml.sh
棒材类型为:金属
福的尺寸是:1789

编辑3另一位用户说他们在FreeBSD中遇到问题,建议保存读取的退出状态,并在读取结束时返回,如:

读取dom(){
本地IFS=\>
读取-d\<实体内容
本地RET=$?
TAG_NAME=${ENTITY%%*}
属性=${ENTITY}
返回$RET
}

我看不出有什么理由不起作用

发表评论