我必须根据参数替换 XML 标记中的一些属性内容$1
。
我们有输入,例如:
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true">
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="true">
如果testname
属性有不是包含$1
,然后将该enabled
值替换为false
;否则 (testname
做包含$1
),将该enabled
值替换为true
.
笔记:可能会遇到其他属性,比本例中的属性更多。
我想过,sed
但也许其他工具可以做得更好?
答案1
还没人说过,所以我会的。请不要使用正则表达式解析 XML。 XML 是一种上下文语言,而正则表达式则不是。这意味着您创建了脆弱的代码,有一天可能会乱七八糟地崩溃。
更多示例请参见:https://stackoverflow.com/questions/1732348/regex-match-open-tags- except-xhtml-self-contained-tags
请使用解析器。它们存在于多种语言中 - 就我个人而言,我喜欢perl
,并且您的任务有点像这样:
#!/usr/bin/env perl
use strict;
use warnings;
#parser library
use XML::Twig;
#ingest data
my $twig = XML::Twig -> parse (\*DATA);
#iterate all tags <ThreadGroup>
foreach my $group ( $twig -> get_xpath('//ThreadGroup') ) {
#check testname regex match
if ( $group -> att('testname') =~ /AA/ ) {
#set enabled
$group -> set_att('enabled', 'true');
}
else {
#set disabled
$group -> set_att('enabled', 'false');
}
}
#pretty print options vary, see man page.
$twig -> set_pretty_print('indented_a');
$twig -> print;
__DATA__
<xml>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true" />
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="true" />
</xml>
是的 - 它是必须使用 XML 解析器,因为正则表达式无法安全地完成此操作。 XML 中的许多内容在语义上是相同的,例如属性排序、换行、一元标记等,但它们的正则表达式不同。但解析器不会被它抓住。
如果您愿意,以上内容可以缩减为一行:
perl -MXML::Twig -e 'XML::Twig -> new ( twig_handlers => { ThreadGroup => sub { $_ -> set_att("enabled", $_ -> att("testname") =~ /AA/ ? "true" : "false" ) } } ) -> parsefile_inplace("yourfile")'
您的系统管理团队应该感谢您这样做(这并不是说他们会这样做),因为任何基于正则表达式的解决方案可能有一天会无缘无故地崩溃。
作为一个最简单的例子 - 您的 XML 在语义上与以下内容相同:
<xml>
<ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="OO CSS DPM PRI"
/>
<ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="AA CSS DPM PRI"
/>
</xml>
或者:
<xml>
<ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="OO CSS DPM PRI"/>
<ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGroup"
testname="AA CSS DPM PRI"/>
</xml>
或者:
<xml><ThreadGroup enabled="true" guiclass="ThreadGroupGui" testclass="ThreadGrou
p" testname="OO CSS DPM PRI"/><ThreadGroup enabled="true" guiclass="ThreadGroupG
ui" testclass="ThreadGroup" testname="AA CSS DPM PRI"/></xml>
或者:
<xml
><ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="OO CSS DPM PRI"
/><ThreadGroup
enabled="true"
guiclass="ThreadGroupGui"
testclass="ThreadGroup"
testname="AA CSS DPM PRI"
/></xml>
那是在我们进入属性排序、可能的标签嵌套或在您意想不到的地方“匹配”的其他子字符串之前。
答案2
使用XML小星:
#!/bin/sh
xml ed -u "//ThreadGroup[. = contains(@testname, '$1')]/@enabled" -v "true"
-u "//ThreadGroup[. = not(contains(@testname, '$1'))]/@enabled" -v "false"
假设您的 XML 是有效的(我添加了一个根标记,并用<SomeTag>
正确分隔了空节点。我还将属性设置为,以便脚本实际上<ThreadGroup>
/>
enabled
"hello"
做某物):
$ cat data.xml
<SomeTag>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="hello"/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="hello"/>
</SomeTag>
$ sh script.sh "OO" <data.xml
<?xml version="1.0"?>
<SomeTag>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="OO CSS DPM PRI" enabled="true"/>
<ThreadGroup guiclass="ThreadGroupGui" testclass="ThreadGroup" testname="AA CSS DPM PRI" enabled="false"/>
</SomeTag>
答案3
如果我正确理解逻辑,这个 sed 命令会在值内搜索给定的 $1 参数testname
;如果存在,则搜索该enabled
值并将其从 false 替换为 true。如果 ( ) 不存在!
,则将该enabled
值从 true 替换为 false。
sed '/ testname="[^"]*'"$1"'[^"]*"/ s/ enabled="false"/ enabled="true"/;
/ testname="[^"]*'"$1"'[^"]*"/!s/ enabled="true"/ enabled="false"/' input > output
我尝试通过在属性名称(testname 和enabled)之前添加前导空格并使用字符类来帮助正则表达式匹配[^"]
。
答案4
#/bin/sh
sed -r 'h
s/.*testname="([^"]*)".*/\1/
/\b'"$1"'\b/{ g; s/enabled="[^"]*"/enabled="true"/; b}
g
s/enabled="[^"]*"/enabled="false"/
' file