Let's avoid boring approaches like reading the whole line as a string in some scripting language and doing a regex substitution with embedded newlines.
State machine approach in Awk. Each line is printed. If the three required lines appear consecutively, the state
variable increments to 2 and the required block is printed. For any other input, state
lapses to zero.
1
1 { print state }
state == 0 && /^<stringProp name="TestPlan.user_define_classpath"><\/stringProp>$/ {
state++; next
}
state == 1 && /^<\/TestPlan>$/ {
state++; next
}
state == 2 && /^<hashTree>$/ {
print \
"<org.apache.jmeter.config.RestServerNodeElement guiclass=\"TestBeanGUI\"\n" \
"testclass=\"org.apache.jmeter.config.RestServerNodeElement\"\n" \
"testname=\"ATC_Test_Lab\" enabled=\"true\">\n" \
"<stringProp name=\"TestPlan.comments\"> </stringProp>\n" \
"<stringProp name=\"authbasic\">${Basic}</stringProp>\n" \
"<stringProp name=\"authpassword\">ENC(dsxxxxxxxxxxxWiu+FCzl5+A==)</stringProp>\n" \
"<stringProp name=\"authuser\">${User}</stringProp>\n" \
"<stringProp name=\"extratag\"></stringProp>\n"
}
1 {
state = 0;
}
Multi-line match and output in TXR. The rare approach here is to use the data
directive to capture two points in the input stream (which is a lazy list of strings) and then the Lisp function ldiff
to get the range of lines between those two points (i.e. the block that was matched) so that it can be output with tprint
:
@(repeat)
@ (cases)
@ (data start)
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
@ (data end)
@ (do (tprint (ldiff start end)))
@ (output)
<org.apache.jmeter.config.RestServerNodeElement guiclass="TestBeanGUI"
testclass="org.apache.jmeter.config.RestServerNodeElement"
testname="ATC_Test_Lab" enabled="true">
<stringProp name="TestPlan.comments"> </stringProp>
<stringProp name="authbasic">${Basic}</stringProp>
<stringProp name="authpassword">ENC(dsxxxxxxxxxxxWiu+FCzl5+A==)</stringProp>
<stringProp name="authuser">${User}</stringProp>
<stringProp name="extratag"></stringProp>
@ (end)
@ (or)
@line
@ (do (put-line line))
@ (end)
@(end)
In this approach we could add some variables into the block of code being matched and then refer to them in the @(output)
. Kind of how like a s/regexp/replace/
can refer to captured pieces via \1
, \2
, &
and so on. Suppose that instead of the static word TestPlan
, a piece of text had to be extracted, and the duplicated in all the places where TestPlan
appears now:
@(repeat)
@ (cases)
@ (data start)
<stringProp name="@TYPE.user_define_classpath"></stringProp>
</@TYPE>
<hashTree>
@ (data end)
@ (do (tprint (ldiff start end)))
@ (output)
<org.apache.jmeter.config.RestServerNodeElement guiclass="TestBeanGUI"
testclass="org.apache.jmeter.config.RestServerNodeElement"
testname="ATC_Test_Lab" enabled="true">
<stringProp name="@TYPE.comments"> </stringProp>
<stringProp name="authbasic">${Basic}</stringProp>
<stringProp name="authpassword">ENC(dsxxxxxxxxxxxWiu+FCzl5+A==)</stringProp>
<stringProp name="authuser">${User}</stringProp>
<stringProp name="extratag"></stringProp>
@ (end)
@ (or)
@line
@ (do (put-line line))
@ (end)
@(end)
Let's try this version:
$ txr add.txr -
foo
foo
bar
bar
blah
blah
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<stringProp name="TestPlan.user_define_classpath"></stringProp>
</TestPlan>
<hashTree>
<org.apache.jmeter.config.RestServerNodeElement guiclass="TestBeanGUI"
testclass="org.apache.jmeter.config.RestServerNodeElement"
testname="ATC_Test_Lab" enabled="true">
<stringProp name="TestPlan.comments"> </stringProp>
<stringProp name="authbasic">${Basic}</stringProp>
<stringProp name="authpassword">ENC(dsxxxxxxxxxxxWiu+FCzl5+A==)</stringProp>
<stringProp name="authuser">${User}</stringProp>
<stringProp name="extratag"></stringProp>
blah
blah
blah
blah
<stringProp name="EvilPlan.user_define_classpath"></stringProp>
</EvilPlan>
<hashTree>
<stringProp name="EvilPlan.user_define_classpath"></stringProp>
</EvilPlan>
<hashTree>
<org.apache.jmeter.config.RestServerNodeElement guiclass="TestBeanGUI"
testclass="org.apache.jmeter.config.RestServerNodeElement"
testname="ATC_Test_Lab" enabled="true">
<stringProp name="EvilPlan.comments"> </stringProp>
<stringProp name="authbasic">${Basic}</stringProp>
<stringProp name="authpassword">ENC(dsxxxxxxxxxxxWiu+FCzl5+A==)</stringProp>
<stringProp name="authuser">${User}</stringProp>
<stringProp name="extratag"></stringProp>
x
x
y
y
z
z
When TestPlan
is changed to EvilPlan
, it works nicely, and the code is readable: the multi-line text is represented as-is. If the
@
character occurs, it has to be doubled up as @@
, but it didn't occur.
/
for example:sed 's#search#replace#'
– Sundeep Sep 7 '16 at 6:49