Reputation: 152
My file looks like this:
FooBarA
foo bar
foo = bar
FooBarB
foo bar
foo = bar
FooBarC
foo bar
foo = bar
...
What I would like to do is to write a script that replaces the bar
in foo = bar
but only if it belongs to FooBarB
. So in the example above only the second bar
out of all foo = bar
lines should be replaced.
I've played around with sed
but I just can't get it done right. I would also like to avoid installing any tools that aren't necessarily pre-installed on the system (I'm on Mac OS), since the script will be used by other team members too.
Upvotes: 1
Views: 657
Reputation: 58430
This might work for you (GNU sed):
sed '/FooBarB/{:a;n;/^$/b;/foo = bar/!ba;s//foo = baz/}' file
Match on the string FooBarB
and start a loop.
Fetch the next line and study it.
If the line is empty the stanza is done, so break out of the loop.
If the line does not contains the string foo = bar
, fetch the next line and continue the loop.
Otherwise, substitute the new value for bar
and finish the loop.
Alternative (which may work for macos users?):
sed -e '/FooBarB/{:a' -e 'n;/^$/b;/foo = bar/!ba;s//foo = baz/;}' file
Since the OP changed the input data to the question another solution:
sed '/FooBar/h;G;/FooBarB/s/foo = bar/foo = baz/;P;d' file
Upvotes: 2
Reputation: 203655
Using any awk in any shell on every Unix box:
$ awk -v tgt='FooBarB' -v val='whatever' '
NF==1{tag=$0} (NF>1) && (tag==tgt) && sub(/=.*/,"= "){$0=$0 val}
1' file
FooBarA
foo bar
foo = bar
FooBarB
foo bar
foo = whatever
FooBarC
foo bar
foo = bar
Upvotes: 1
Reputation: 179119
One way to do it with sed
(tested using macOS's sed and GNU sed), would be this:
#!/usr/bin/env sed -Ef
/FooBarB/,/^FooBar/ {
s/(foo[[:space:]]*=[[:space:]]*).+/\1new-value/
}
Here's what it does:
/FooBarB/,/^FooBar/
matches a range of lines where the first line matches the regex /FooBarB/
and the last line matches the regex /^FooBar/
(which is the start of the next "group"). The comma between the two regexes is the syntax for range matching in sed
.
s/(foo[[:space:]]*=[[:space:]]*).+/\1new-value/
— [s]ubstitutes (in the matched range of lines) whatever matches the regex (foo[[:space:]]*=[[:space:]]*).+
with \1new-value
, where \1
references the first capturing group in the search regex. The search regex looks for foo
followed by optional whitespace, followed by an =
sign, followed again by whitespace and then whatever else is there, which in your case is the old value.
You could do it all in just one line, but I wanted to show a version that's a bit more digestible (as far as sed
goes, in any case):
sed -E '/FooBarA/,/^FooBar/s/(foo[[:space:]]*=[[:space:]]*).+/\1new-value/' temp.md
Upvotes: 2
Reputation: 13249
For reference, the GNU awk variant:
awk -v v="newvalue" 'BEGIN{FS=OFS="\n";RS=ORS="\n\n"}$1=="FooBarB"{$3="foo = " v}1' file
By using the option -v
, the variable v
holds the wanted string.
The BEGIN
statement sets respectively the input, output field separator, the input and output record separator to one and two carriage return.
That way a record is composed of the block of several lines containing the pattern Foobar[ABC]
.
The last statement sets the new value by rewriting the third line.
Upvotes: 0