Bash 脚本中的多行正则表达式 XML 替换(可能通过 Perl)
Multiline Regex XML replacement in Bash script (likely via Perl)
我有一个 XML 文件,格式如下。
我想利用步骤名称,其中包含 VM 相关步骤中的字符 "VM"。当我在步骤名称中看到 "VM" 时,我想将计算机名称字段替换为 "VMname" 并将计算机 ID 字段替换为 "VMid."
我要
<step sequence="106" name="Patch%20Baseline">
<!-- No parameters accepted for baseline steps -->
<target-set>
<computer name="nameAndIDTarget" id="123" />
</target-set>
</step>
<step sequence="110" name="Warning%3A%20Outdated%20VMware%20Tools%20Version">
<target-set>
<computer name="nameAndIDTarget" id="123" />
</target-set>
</step>
成为
<step sequence="106" name="Patch%20Baseline">
<!-- No parameters accepted for baseline steps -->
<target-set>
<computer name="nameAndIDTarget" id="123" />
</target-set>
</step>
<step sequence="110" name="Warning%3A%20Outdated%20VMware%20Tools%20Version">
<target-set>
<computer name="VMname" id="VMid" />
</target-set>
</step>
此函数将成为 bash 脚本的一部分,因此如果通过 Perl 完成,我更喜欢单行或内联方法。我是 Perl/Regex 愚蠢的,无法理解我迄今为止看到的例子。
给你
perl -pe 'if (m!<step .*name=".*VM!..m!</step>!){ if (m!<computer!){s/name=".*?"/name="VMname"/;s/id=".*?"/id="VMid"/} }' test.xml
留下参考以防投票的人不知道 Perl 魔法
perldoc flip-flop
这是我的解决方案 - 在我看来,这对于 one-liner 来说太复杂了。只需要调用一个perl脚本就很容易了。
$vm = ; # $vm is set to 1 only if s step sequence with `VM` in is detected
while(<>) { # Loop through the file, line by line
if(m!<step sequence=.* name="(.*VM.*)">!) { # Check for sequence name containing VM
$vm = 1;
}
if($vm == 1 && m!<computer name!) { # Output special VM name
print qq! <computer name="VMname" id="VMid" />\n!;
$vm = 0; # Reset the VM flag
} else {
print $_; # Otherwise print the input line
}
}
它没有回答 所问的问题 ,但正确的方法是使用 XML-aware 工具,例如 XMLStarlet:
xmlstarlet ed -u '//step[contains(@name, "VM")]/target-set/computer/@name' -v VMname \
-u '//step[contains(@name, "VM")]/target-set/computer/@id' -v VMid \
<in.xml >out.xml
请注意,这假定使用默认命名空间;如果在您的层次结构中更高的某个地方有一个 xmlns="..."
属性,则应该编辑问题以包括该属性。
我有一个 XML 文件,格式如下。
我想利用步骤名称,其中包含 VM 相关步骤中的字符 "VM"。当我在步骤名称中看到 "VM" 时,我想将计算机名称字段替换为 "VMname" 并将计算机 ID 字段替换为 "VMid."
我要
<step sequence="106" name="Patch%20Baseline">
<!-- No parameters accepted for baseline steps -->
<target-set>
<computer name="nameAndIDTarget" id="123" />
</target-set>
</step>
<step sequence="110" name="Warning%3A%20Outdated%20VMware%20Tools%20Version">
<target-set>
<computer name="nameAndIDTarget" id="123" />
</target-set>
</step>
成为
<step sequence="106" name="Patch%20Baseline">
<!-- No parameters accepted for baseline steps -->
<target-set>
<computer name="nameAndIDTarget" id="123" />
</target-set>
</step>
<step sequence="110" name="Warning%3A%20Outdated%20VMware%20Tools%20Version">
<target-set>
<computer name="VMname" id="VMid" />
</target-set>
</step>
此函数将成为 bash 脚本的一部分,因此如果通过 Perl 完成,我更喜欢单行或内联方法。我是 Perl/Regex 愚蠢的,无法理解我迄今为止看到的例子。
给你
perl -pe 'if (m!<step .*name=".*VM!..m!</step>!){ if (m!<computer!){s/name=".*?"/name="VMname"/;s/id=".*?"/id="VMid"/} }' test.xml
留下参考以防投票的人不知道 Perl 魔法
perldoc flip-flop
这是我的解决方案 - 在我看来,这对于 one-liner 来说太复杂了。只需要调用一个perl脚本就很容易了。
$vm = ; # $vm is set to 1 only if s step sequence with `VM` in is detected
while(<>) { # Loop through the file, line by line
if(m!<step sequence=.* name="(.*VM.*)">!) { # Check for sequence name containing VM
$vm = 1;
}
if($vm == 1 && m!<computer name!) { # Output special VM name
print qq! <computer name="VMname" id="VMid" />\n!;
$vm = 0; # Reset the VM flag
} else {
print $_; # Otherwise print the input line
}
}
它没有回答 所问的问题 ,但正确的方法是使用 XML-aware 工具,例如 XMLStarlet:
xmlstarlet ed -u '//step[contains(@name, "VM")]/target-set/computer/@name' -v VMname \
-u '//step[contains(@name, "VM")]/target-set/computer/@id' -v VMid \
<in.xml >out.xml
请注意,这假定使用默认命名空间;如果在您的层次结构中更高的某个地方有一个 xmlns="..."
属性,则应该编辑问题以包括该属性。