regex - 为什么 Perl 在前面没有最后一部分时替换文本:*. * .?
问题描述
我想通过 Perl 删除部分 xml 文件(JMeter 测试计划的元素),它们是:
<hashTree>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="SON-ASS" enabled="true">
<stringProp name="JSON_PATH">$.status</stringProp>
<stringProp name="EXPECTED_VALUE">ok</stringProp>
<boolProp name="JSONVALIDATION">true</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">false</boolProp>
</JSONPathAssertion>
<hashTree/>
</hashTree>
第一次JSONPathAssertion
发生在tst.jmx
:
grep -A 10 JSONPath test7.jmx | head -n 10
:
</HTTPSamplerProxy>
<hashTree>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="SON-ASS" enabled="true">
<stringProp name="JSON_PATH">$.status</stringProp>
<stringProp name="EXPECTED_VALUE">ok</stringProp>
<boolProp name="JSONVALIDATION">true</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">false</boolProp>
</JSONPathAssertion>
<hashTree/>
<JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="JSR223 PreProcessor" enabled="true">
</hashTree>.
仅第一次出现后没有<hashTree/>.
空格分隔。
然后我写:
cat test7.jmx | perl -0777pe 's` *<hashTree>. *<JSONPathAssertion.*?</JSONPathAssertion>. *<hashTree/>. *</hashTree>.``gs' > test7_1.jmx
然后grep -A 10 JSONPath test7_1.jmx | head -n 10
现在有空输出。结果文件根本没有JSONPathAssertion
。为什么那个特定的事件被替换了?
PS 可能值得单独提出一个问题,但我无法找到如何在 perl 中匹配单个换行符,仅作为较大模式的一部分:
评论后添加:
下面的完整文件 test7.jmx(再次测试从 SO 复制内容并将 vi 粘贴到新文件),顺便说一句MacOS Mojave
,在 CentOS 7 上确认的一次迭代中全部完成:
<?xml version="1.0" encoding="UTF-8"?>
<jmeterTestPlan version="1.2" properties="5.0" jmeter="5.1.1 r1855137">
<hashTree>
<TestPlan guiclass="TestPlanGui" testclass="TestPlan" testname="test one" enabled="true">
<stringProp name="TestPlan.comments"></stringProp>
<boolProp name="TestPlan.functional_mode">false</boolProp>
<boolProp name="TestPlan.tearDown_on_shutdown">true</boolProp>
<boolProp name="TestPlan.serialize_threadgroups">false</boolProp>
<elementProp name="TestPlan.user_defined_variables" elementType="Arguments" guiclass="ArgumentsPanel" testclass="Arguments" testname="User Defined Variables" enabled="true">
<collectionProp name="Arguments.arguments"/>
</elementProp>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup guiclass="com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroupGui" testclass="com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup" testname="Thread - main" enabled="true">
<elementProp name="ThreadGroup.main_controller" elementType="com.blazemeter.jmeter.control.VirtualUserController"/>
<stringProp name="ThreadGroup.on_sample_error">continue</stringProp>
<stringProp name="TargetLevel">${__P(threads, 1)}</stringProp>
<stringProp name="RampUp">${__P(time, 1)}</stringProp>
<stringProp name="Steps">${__P(steps, 1)}</stringProp>
<stringProp name="Hold">${__P(hold, 3)}</stringProp>
<stringProp name="LogFilename"></stringProp>
<stringProp name="Iterations"></stringProp>
<stringProp name="Unit">M</stringProp>
</com.blazemeter.jmeter.threads.concurrency.ConcurrencyThreadGroup>
<hashTree>
<GenericController guiclass="LogicControllerGui" testclass="GenericController" testname="Simple Controller - main" enabled="true"/>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="REQUEST1" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{
"version": "1.1",
"test": {
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="JSON Assertion" enabled="true">
<stringProp name="JSON_PATH">$.status</stringProp>
<stringProp name="EXPECTED_VALUE">ok</stringProp>
<boolProp name="JSONVALIDATION">true</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">false</boolProp>
</JSONPathAssertion>
<hashTree/>
<JSR223PreProcessor guiclass="TestBeanGUI" testclass="JSR223PreProcessor" testname="JSR223 PreProcessor" enabled="true">
<stringProp name="cacheKey">true</stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<stringProp name="script">// period in the past - year-month-day, set from properties in User Defined Variables
import java.time.Instant;
import java.time.temporal.ChronoUnit;
import groovy.json.JsonOutput;
import org.apache.commons.lang.RandomStringUtils;</stringProp>
<stringProp name="scriptLanguage">groovy</stringProp>
</JSR223PreProcessor>
<hashTree/>
<JSR223PostProcessor guiclass="TestBeanGUI" testclass="JSR223PostProcessor" testname="JSR223 PostProcessor" enabled="true">
<stringProp name="cacheKey">true</stringProp>
<stringProp name="filename"></stringProp>
<stringProp name="parameters"></stringProp>
<stringProp name="script">import java.time.Instant;
import java.time.temporal.ChronoUnit;</stringProp>
<stringProp name="scriptLanguage">groovy</stringProp>
</JSR223PostProcessor>
<hashTree/>
</hashTree>
<IfController guiclass="IfControllerPanel" testclass="IfController" testname="If Controller" enabled="true">
<stringProp name="IfController.condition">${__groovy(${random_variable} == 1)}</stringProp>
<boolProp name="IfController.evaluateAll">false</boolProp>
<boolProp name="IfController.useExpression">true</boolProp>
</IfController>
<hashTree>
<RandomController guiclass="RandomControlGui" testclass="RandomController" testname="Random Controller" enabled="true">
<intProp name="InterleaveControl.style">1</intProp>
</RandomController>
<hashTree>
<HTTPSamplerProxy guiclass="HttpTestSampleGui" testclass="HTTPSamplerProxy" testname="REQUEST2" enabled="true">
<boolProp name="HTTPSampler.postBodyRaw">true</boolProp>
<elementProp name="HTTPsampler.Arguments" elementType="Arguments">
<collectionProp name="Arguments.arguments">
<elementProp name="" elementType="HTTPArgument">
<boolProp name="HTTPArgument.always_encode">false</boolProp>
<stringProp name="Argument.value">{
"version": "1.1",
"test": {
}</stringProp>
<stringProp name="Argument.metadata">=</stringProp>
</elementProp>
</collectionProp>
</elementProp>
<stringProp name="HTTPSampler.domain"></stringProp>
<stringProp name="HTTPSampler.port"></stringProp>
<stringProp name="HTTPSampler.protocol"></stringProp>
<stringProp name="HTTPSampler.contentEncoding"></stringProp>
<stringProp name="HTTPSampler.path"></stringProp>
<stringProp name="HTTPSampler.method">POST</stringProp>
<boolProp name="HTTPSampler.follow_redirects">false</boolProp>
<boolProp name="HTTPSampler.auto_redirects">false</boolProp>
<boolProp name="HTTPSampler.use_keepalive">true</boolProp>
<boolProp name="HTTPSampler.DO_MULTIPART_POST">false</boolProp>
<stringProp name="HTTPSampler.embedded_url_re"></stringProp>
<stringProp name="HTTPSampler.connect_timeout"></stringProp>
<stringProp name="HTTPSampler.response_timeout"></stringProp>
</HTTPSamplerProxy>
<hashTree>
<JSONPathAssertion guiclass="JSONPathAssertionGui" testclass="JSONPathAssertion" testname="JSON Assertion" enabled="true">
<stringProp name="JSON_PATH">$.status</stringProp>
<stringProp name="EXPECTED_VALUE">ok</stringProp>
<boolProp name="JSONVALIDATION">true</boolProp>
<boolProp name="EXPECT_NULL">false</boolProp>
<boolProp name="INVERT">false</boolProp>
<boolProp name="ISREGEX">false</boolProp>
</JSONPathAssertion>
<hashTree/>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</hashTree>
</jmeterTestPlan>
解决方案
这显示了正在匹配的内容。文本的整个突出显示部分只是一个匹配项。匹配的.*?
比您预期的要多。
我会使用适当的 XML 解析器。
use XML::LibXML qw( );
my $xpath = "
//hashTree[
count(*)=2 and
*[position()=1 and name()='JSONPathAssertion'] and
*[position()=2 and name()='hashTree' and count(*)=0]
]
";
my $doc = XML::LibXML->new->parse_file("a.xml");
$_->unlink for $doc->findnodes($xpath);
$doc->toFile("b.xml");
或者
use XML::LibXML qw( );
my $doc = XML::LibXML->new->parse_file("a.xml");
for my $node ($doc->findnodes("//hashTree")) {
my @child_eles = $node->findnodes("*");
$node->unlink
if @child_eles == 2
&& $child_eles[0]->nodeName eq "JSONPathAssertion"
&& $child_eles[1]->nodeName eq "hashTree"
&& $child_eles[1]->findnodes("*")->size == 0;
}
$doc->toFile("b.xml");
推荐阅读
- python - Pygame_menu 循环覆盖到另一个屏幕的过渡
- javascript - 获取两个数组之间的差异
- javascript - 如何在条件项内使用链接和路由
- r - 将正态分布拟合到分组数据,给出预期频率
- python - 在 JSON 列表/对象 Python 中搜索参数列表
- javascript - 如何从 json 表中删除?
- flask-mysql - 如何将python烧瓶中的多个变量传递给mysqldb?
- javascript - 如何在反应挂钩中将回调 url 形成查询字符串
- python - 从复杂的 json 内容中获取值
- mysql - 在不使用交叉连接(MySQL 5.6)的情况下,如何在提供的示例数据中为两个字段的所有不同条目实现笛卡尔积?