I have two lines in the file like below:
#foo:
# bar=<need replacement>
I'd like to remove the #
if it exists(uncomment the line) for the two lines when the first line starting with foo
and the second line contains bar
and replace the text between <>
.
Can I do this in one pass with N;P;D
cycle using sed? Or is there any other simpler way to do it?
CodePudding user response:
This might work for you (GNU sed):
sed -E '/^#?foo/{N;/\n.*bar/{s/^#//mg;s/(\n.*<).*(>.*)/\1replace\2/};P;D}' file
If the current line begins with foo
commented or uncommented, then:
Append the following line
If the following line contains the word
bar
, then:- Delete any leading
#
in either of the two lines. - Replace any characters between
<
and>
on the second line withreplace
.
- Delete any leading
Print/delete the first of the two lines and repeat.
N.B. This allows a leading foo
followed by another leading foo
followed by bar
to be processed.
CodePudding user response:
Using a POSIX sed with a standard N;…;P;D;
loop and basic regexes
(BRE):
sed -e '$!N' -e '/^#\(foo:\n\)#\([[:blank:]]*bar\)=.*/ s//\1\2=newvalue/' -e P -e D < file
CodePudding user response:
A piped sed
may be needed if your data contains multiple lines of the text to be matched. e.g:
cat input_file
#foo:
# bar=<need replacement>
#bar:
# foo=<dont replace>
#foo:
# bin=<dont replace>
#foo:
# bar=<need replacement>
#foo:
# bash=<dont replace>
The first sed
will deal with the condition of matching #foo
, if it exist, then it will check the second condition that a bar
matches on the following line. If both conditions are met, the n
will then substitute #
from the match on the line that matched bar
and will also change the contents of need replacement
to test
.
The second sed
pipe will use the same conditions, but rather than n
to check and change the next line, I will use N
to check the condition of the next line, but to change the first line if the condition is met and remove #
sed -E '/^#foo/ {n;/bar/ s/^#(.*=.).*(.)/\1test\2/g}' input_file | sed '/^#foo/ {N;/bar/ s/^#//g}'
Output
foo:
bar=<test>
#bar:
# foo=<dont replace>
#foo:
# bin=<dont replace>
foo:
bar=<test>
#foo:
# bash=<dont replace>
CodePudding user response:
It can probably be done, but I would use a different tool. How about Awk?
awk -v replace="new value" 'm && /^[^:] :/ { m=0 }
/^#foo:/ { m=1 }
m { sub(/^#/, ""); s(/<[^<>]*>/, replace) }1'
This follows the familiar pattern of setting a state variable m
when we see the beginning of the interesting region, performing the replacements when we are in the region, and turning off the variable when we see the beginning of a new region (speculating somewhat on what the condition for that might look like).
It's not hard to alter this to simply look for a fixed number of lines after the start of the region; set the state variable to a number, and decrement it for each line.
If this is a Makefile, using a make
variable would be a lot more elegant and robust, though.
ifdef ENABLE
foo:
bar=$(barvalue)
endif
(or perhaps simply ifdef barvalue
!)