在标记HTML之间更改内容



我有一个xml文件,如下

<firewall>
<custom_list max_entry_num="50">
<id>1</id>
<desc>first</desc>
<rule>!!!!!</rule>
</custom_list>
<custom_list max_entry_num="50">
<id>2</id>
<desc>seconde</desc>
<rule> !!!!!! </rule>
</custom_list>
</firewall>

我想更改标签之间的内容,为此我使用以下命令:

xmlstartlet ed -u "/firewall/custom_list/rule' -v "My First Text" old.xml >new.xml

但是这个文本在两个标签中,当id=1和id=2。

<custom_list max_entry_num="50">
<id>1</id>
<desc>first</desc>
<rule>MY FIRST TEXT</rule>
</custom_list>
<custom_list max_entry_num="50">
<id>2</id>
<desc>second</desc>
<rule>MY FIRST TEXT</rule>

我想:

  • id等于1时,我放入"MY FIRST TEXT"
  • id等于2时,我放入"MY SECOND TEXT"

如何在bash中执行此操作?

您可以使用sed/awk来解决这个问题。

使用纯bash可以工作,但容易失败:

declare id=''
declare can_match_id=no
while read -r line; do
if [[ $line == *'</custom_list>'* ]]; then 
id='' can_match_id=no
echo "$line"
elif [[ $line == *'<custom_list>'* ]]; then 
can_match_id=yes
echo "$line"
elif [[ $can_match_id == yes && "$line" =~ <id>([0-9]+)</id> ]]; then
id="${BASH_REMATCH[1]}"
echo "$line"
can_match_id=no
elif [[ -n "$id" && "$line" =~ ^(.*<rule>)([^<]*)(</rule>.*)$ ]]; then
declare text=""
case "$id" in
1) text="TEXT FOR ID=1" ;;
2) text="TEXT FOR ID=2" ;;
*) text="${BASH_REMATCH[2]}" ;;
esac
id='' # reinit
echo "${BASH_REMATCH[1]}${text}${BASH_REMATCH[2]}"
else 
echo "$line"
fi
done < old.xml

我显然没有测试它。

我认为,如果您使用XML,您应该使用最了解XML的工具:XSLT。

  1. Perl可能有一个处理XSLT的模块
  2. Saxon在Java/C/C++中可用https://www.saxonica.com/download/download_page.xml并处理XSLT3.0
  3. xmlstarlet虽然很旧,但它处理XSLT1.0

下面的样式表可能与xmlstarlet一起使用。。。但是XSLT1.0不是我喜欢的,您最好将Saxon用于XSLT3.0。

<?xml version="1.0"?>
<xsl:stylesheet version="1.0" xmlns:beans="http://www.springframework.org/schema/beans" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" exclude-result-prefixes="beans">
<xsl:template match="@*|node()">
<xsl:copy>
<xsl:apply-templates select="@*|node()"/>
</xsl:copy>
</xsl:template>
<xsl:template match="//custom_list">
<xsl:element name="{local-name()}">
<xsl:copy-of select="@*" />
<xsl:variable name="id" select="id/text()" />
<xsl:for-each select="*">
<xsl:choose>
<xsl:when test="local-name() = 'rule'">
<xsl:element name="{local-name()}">
<xsl:choose>
<xsl:when name="$id = '1'">VALUE FOR ID 1</xsl:when>
<xsl:when name="$id = '2'">VALUE FOR ID 2</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:element>
</xsl:when>
<xsl:when test="local-name() = 'rule' and $id='2' ">
<xsl:element name="rule">VALUE FOR ID 2</xsl:element>
</xsl:when>
<xsl:otherwise>
<xsl:apply-templates select="."/>
</xsl:otherwise>
</xsl:choose>
</xsl:for-each>
</xsl:element>
</xsl:template>
</xsl:stylesheet>

要进行转换,请使用调用xml

xmlstartlet  tr stylesheet.xsl < old.xml > new.xml

这应该进行XSLT1.0转换(由于样式表的原因而失败(。

如果ed可用/可接受。它不是像xmlstarlet之类的xml解析器/编辑器。

给定file.xml文件。

<firewall>
<custom_list max_entry_num="50">
<id>1</id>
<desc>first</desc>
<rule>!!!!!</rule>
</custom_list>
<custom_list max_entry_num="50">
<id>2</id>
<desc>seconde</desc>
<rule> !!!!!! </rule>
</custom_list>
</firewall>

脚本。

#!/usr/bin/env sh
ed -s file.xml <<-'EOF'
/<firewall>/;/</firewall>/;?<id>1</id>?;/</custom_list>/s/(<rule>).*(</rule>)/1MY FIRST TEXT2/
/<firewall>/;/</firewall>/;?<id>2</id>?;/</custom_list>/s/(<rule>).*(</rule>)/1MY SECOND TEXT2/
w newfile.xml
,p
Q
EOF

也可以使用ed脚本。我们就叫它script.ed

/<firewall>/;/</firewall>/;?<id>1</id>?;/</custom_list>/s/(<rule>).*(</rule>)/1MY FIRST TEXT2/
/<firewall>/;/</firewall>/;?<id>2</id>?;/</custom_list>/s/(<rule>).*(</rule>)/1MY SECOND TEXT2/
w newfile.xml
,p
Q

然后运行

ed -s file.xml < script.ed

输出

<firewall>
<custom_list max_entry_num="50">
<id>1</id>
<desc>first</desc>
<rule>MY FIRST TEXT</rule>
</custom_list>
<custom_list max_entry_num="50">
<id>2</id>
<desc>seconde</desc>
<rule>MY SECOND TEXT</rule>
</custom_list>
</firewall>

,p只是向stdout显示输出,如果需要,将其删除。

两个解决方案都将创建一个名为newfile.xml的新文件,将其更改为其他文件。

xmlstarlet ed -u "/firewall/custom_list/rule[../id = '1']" -v "My First Text" -u "/firewall/custom_list/rule[../id = '2']" -v "My second Text" old.xml > new.xml

最新更新