如何用xmlstarlet插入含引号、撇号并转义为实体的文本?
XML节点文本的引号与撇号转义需求
我写了一个bash脚本,要把文本输出到XML节点里。目前<、>、&已经能正常转义,但还想把引号(")和撇号(')也转成XML实体("、')。
操作示例及当前结果
$ inxi -S|tail -3 Kernel: 5.19.16-100.fc35.x86_64 arch: x86_64 bits: 64 Console: pty pts/1 Distro: Fedora release 35 (Thirty Five) $ xmlstarlet --version compiled against libxml2 2.9.12, linked with 20914 compiled against libxslt 1.1.34, linked with 10135 $ printf '<foo>\n <bar></bar>\n</foo>\n' | tee test.xml; <foo> <bar></bar> </foo> $ xmlstarlet ed --inplace -u "//bar" -v "I'd like <this> text to be \"escaped\"." test.xml ; cat test.xml <?xml version="1.0"?> <foo> <bar>I'd like <this> text to be "escaped".</bar> </foo> $ xmlstarlet ed --inplace -u "//bar" -v "I&apos;d like <this> text to be &quot;escaped&quot;." test.xml ; cat test.xml <?xml version="1.0"?> <foo> <bar>I&amp;apos;d like <this> text to be &amp;quot;escaped&amp;quot;.</bar> </foo>
期望结果
<?xml version="1.0"?> <foo> <bar>I&apos;d like <this> text to be &quot;escaped&quot;.</bar> </foo>
请问能不能用xmlstarlet或者Fedora官方仓库里的其他XML工具实现这种转义效果?如果xmlstarlet不支持,是不是先按第一种方式插入文本,再用perl、sed等后处理是最优方案?
解决方案
一、xmlstarlet原生实现:自定义XSLT模板
xmlstarlet的ed命令默认只会强制转义<、>、&,不会自动处理引号和撇号。可以通过自定义XSLT模板实现完整实体转义:
- 创建
escape.xsl模板文件:
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform"> <xsl:output method="xml" encoding="UTF-8" indent="yes"/> <xsl:template match="@*|node()"> <xsl:copy> <xsl:apply-templates select="@*|node()"/> </xsl:copy> </xsl:template> <xsl:template match="bar/text()"> <xsl:call-template name="escape-xml"> <xsl:with-param name="text" select="."/> </xsl:call-template> </xsl:template> <xsl:template name="escape-xml"> <xsl:param name="text"/> <xsl:if test="$text != ''"> <xsl:variable name="char" select="substring($text, 1, 1)"/> <xsl:choose> <xsl:when test="$char = '&'">&amp;</xsl:when> <xsl:when test="$char = '<'">&lt;</xsl:when> <xsl:when test="$char = '>'">&gt;</xsl:when> <xsl:when test="$char = '"'">&quot;</xsl:when> <xsl:when test="$char = '''">&apos;</xsl:when> <xsl:otherwise><xsl:value-of select="$char"/></xsl:otherwise> </xsl:choose> <xsl:call-template name="escape-xml"> <xsl:with-param name="text" select="substring($text, 2)"/> </xsl:call-template> </xsl:if> </xsl:template> </xsl:stylesheet>
- 执行插入+转义操作:
# 插入原始文本 xmlstarlet ed --inplace -u "//bar" -v "I'd like <this> text to be \"escaped\"." test.xml # 应用XSLT完成全转义 xmlstarlet tr escape.xsl test.xml > test_escaped.xml && mv test_escaped.xml test.xml
二、后处理方案:perl高效转义(推荐)
如果觉得XSLT繁琐,先按常规方式插入文本,再用perl做后处理是最优选择——perl对正则和XML结构的兼容性更强,比sed更可靠:
# 插入未转义文本 xmlstarlet ed --inplace -u "//bar" -v "I'd like <this> text to be \"escaped\"." test.xml # perl替换引号和撇号为XML实体 perl -pi -e 's/\'\''/&apos;/g; s/"/&quot;/g' test.xml
该命令会精准替换节点内的'为'、"为",不会破坏已转义的<、>等实体。
内容的提问来源于stack exchange,提问作者zpangwin
相关产品推荐
相关产品推荐

