更改标签之间的内容 HTML
change content between tags 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 中执行此操作?
如果 ed
是 available/acceptable。这不是像 xmlstarlet
之类的 xml
parser/editor。
给定 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>\)/MY FIRST TEXT/
/<firewall>/;/<\/firewall>/;?<id>2<\/id>?;/<\/custom_list>/s/\(<rule>\).*\(<\/rule>\)/MY SECOND TEXT/
w newfile.xml
,p
Q
EOF
也可以使用 ed
脚本。我们就叫它 script.ed
/<firewall>/;/<\/firewall>/;?<id>1<\/id>?;/<\/custom_list>/s/\(<rule>\).*\(<\/rule>\)/MY FIRST TEXT/
/<firewall>/;/<\/firewall>/;?<id>2<\/id>?;/<\/custom_list>/s/\(<rule>\).*\(<\/rule>\)/MY SECOND TEXT/
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
的新文件,将其更改为其他名称。
您可以使用 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。
- Perl 可能有一个模块来处理 XSLT
- Saxon 在 Java/C/C++ 此处可用 https://www.saxonica.com/download/download_page.xml 并处理 XSLT 3.0。
- xmlstarlet,虽然老了,但处理 XSLT 1.0。
以下样式表可能适用于 xmlstarlet...但是 XSLT 1.0 不是我的菜,您最好使用 Saxon for XSLT 3.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
这应该进行 XSLT 1.0 转换(由于样式表而失败)。
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
我有一个 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 中执行此操作?
如果 ed
是 available/acceptable。这不是像 xmlstarlet
之类的 xml
parser/editor。
给定 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>\)/MY FIRST TEXT/
/<firewall>/;/<\/firewall>/;?<id>2<\/id>?;/<\/custom_list>/s/\(<rule>\).*\(<\/rule>\)/MY SECOND TEXT/
w newfile.xml
,p
Q
EOF
也可以使用 ed
脚本。我们就叫它 script.ed
/<firewall>/;/<\/firewall>/;?<id>1<\/id>?;/<\/custom_list>/s/\(<rule>\).*\(<\/rule>\)/MY FIRST TEXT/
/<firewall>/;/<\/firewall>/;?<id>2<\/id>?;/<\/custom_list>/s/\(<rule>\).*\(<\/rule>\)/MY SECOND TEXT/
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
的新文件,将其更改为其他名称。
您可以使用 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。
- Perl 可能有一个模块来处理 XSLT
- Saxon 在 Java/C/C++ 此处可用 https://www.saxonica.com/download/download_page.xml 并处理 XSLT 3.0。
- xmlstarlet,虽然老了,但处理 XSLT 1.0。
以下样式表可能适用于 xmlstarlet...但是 XSLT 1.0 不是我的菜,您最好使用 Saxon for XSLT 3.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
这应该进行 XSLT 1.0 转换(由于样式表而失败)。
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