修改具有 `type=“instance“` 属性的节点及其结束标签,而不影响其子节点

发布于:2025-06-19 ⋅ 阅读:(13) ⋅ 点赞:(0)

您完全正确,我发现了脚本中的问题。在 Perl 脚本中,单引号和双引号的混合使用导致了变量替换问题。让我修复这个脚本并提供一个更健壮的解决方案:

#!/bin/bash

# 定义要添加的前缀
PREFIX="New_"

for xmlfile in *.xml; do
    # 创建临时文件
    tempfile=$(mktemp)
    
    # 使用 Perl 精确处理 - 修正引号问题
    perl -0777 -pe '
        BEGIN {
            $prefix = "'"$PREFIX"'";  # 正确传递 bash 变量
        }
        
        # 匹配具有 type="instance" 的完整元素
        while (/(<([a-zA-Z0-9_]+)([^>]*\s+type\s*=\s*["\']instance["\'])([^>]*)>(.*?)<\/\2\s*>)/gsi) {
            my $full_match = $1;
            my $tag = $2;
            my $attr = $3;
            my $rest_attr = $4;
            my $content = $5;
            
            # 创建新的开始标签
            my $new_start = "<${prefix}${tag}${attr}${rest_attr}>";
            
            # 创建新的结束标签
            my $new_end = "</${prefix}${tag}>";
            
            # 替换整个元素
            my $new_element = "${new_start}${content}${new_end}";
            s/\Q$full_match\E/$new_element/s;
        }
    ' "$xmlfile" > "$tempfile"
    
    # 用处理后的文件替换原文件
    mv "$tempfile" "$xmlfile"
    
    echo "已处理: $xmlfile"
done

修复的问题

  1. 引号问题

    • 使用 '"$PREFIX"' 正确传递 bash 变量到 Perl
    • 避免单引号内的变量扩展问题
  2. 完整匹配

    • 使用 \Q$full_match\E 确保精确替换原始文本
    • 避免正则表达式元字符导致的意外匹配
  3. 属性处理

    • 正确处理 type="instance" 属性前后的其他属性
    • 保留所有原始属性不变
  4. 嵌套处理

    • 使用 while 循环处理多个匹配项
    • 使用 /s 标志使 . 匹配换行符
    • 使用 /i 标志使匹配不区分大小写

替代方案:使用 XMLStarlet(推荐)

如果可能,我仍然推荐使用 XMLStarlet,因为它更可靠:

#!/bin/bash

# 定义要添加的前缀
PREFIX="New_"

# 检查是否安装了 xmlstarlet
if ! command -v xmlstarlet &> /dev/null; then
    echo "安装 xmlstarlet..."
    # 尝试安装 xmlstarlet
    if command -v apt-get &> /dev/null; then
        sudo apt-get update && sudo apt-get install -y xmlstarlet
    elif command -v yum &> /dev/null; then
        sudo yum install -y xmlstarlet
    elif command -v brew &> /dev/null; then
        brew install xmlstarlet
    else
        echo "错误:无法安装 xmlstarlet,请手动安装"
        exit 1
    fi
fi

for xmlfile in *.xml; do
    # 创建临时文件
    tempfile=$(mktemp)
    
    # 处理 XML 文件 - 只修改具有 type="instance" 的节点
    xmlstarlet ed \
        -r "//*[@type='instance']" -v "${PREFIX}name()" \
        "$xmlfile" > "$tempfile"
    
    # 用处理后的文件替换原文件
    mv "$tempfile" "$xmlfile"
    
    echo "已处理: $xmlfile"
done

为什么推荐 XMLStarlet?

  1. 可靠性

    • 正确处理复杂的 XML 结构
    • 处理嵌套元素、注释、CDATA 等
    • 保持 XML 格式良好
  2. 简单性

    • 一行命令完成所有操作
    • 不需要复杂的正则表达式
  3. 跨平台

    • 在 Linux、macOS 和 Windows (WSL) 上工作
    • 自动处理各种编码和 XML 变体
  4. 安装简单

    • 脚本包含自动安装逻辑
    • 支持多种包管理器

安装 XMLStarlet

如果脚本检测到未安装 XMLStarlet,它会尝试自动安装:

# Debian/Ubuntu
sudo apt-get update && sudo apt-get install -y xmlstarlet

# CentOS/RHEL
sudo yum install -y xmlstarlet

# macOS (使用 Homebrew)
brew install xmlstarlet

测试脚本

要测试脚本,可以创建一个简单的 XML 文件:

<root>
    <element type="instance">
        <child>内容</child>
    </element>
    <other type="other">不应修改</other>
    <container type="instance">
        <nested>嵌套内容</nested>
    </container>
</root>

运行脚本后,应该得到:

<root>
    <New_element type="instance">
        <child>内容</child>
    </New_element>
    <other type="other">不应修改</other>
    <New_container type="instance">
        <nested>嵌套内容</nested>
    </New_container>
</root>

这个修正后的 Perl 脚本解决了引号问题,而 XMLStarlet 方案则提供了最可靠的处理方式。建议优先使用 XMLStarlet 方案以获得最佳结果。


网站公告

今日签到

点亮在社区的每一天
去签到