更改标签之间的内容 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>

我想:

如何在 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。

  1. Perl 可能有一个模块来处理 XSLT
  2. Saxon 在 Java/C/C++ 此处可用 https://www.saxonica.com/download/download_page.xml 并处理 XSLT 3.0。
  3. 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