Reputation: 381
I have a config file that consists of multiple sections and lots of them contain a property with the same name, let it be host
. I need to replace the host
property of one particular section only. Here's the file:
section1 {
...
setting1 = "true"
...
host = "localhost"
...
}
section2 {
...
host = "whatever"
...
}
I want to replace the host
value of section2
with something else. Note that there may be any number of lines in between, marked as ...
Upvotes: 1
Views: 930
Reputation: 67467
awk
to the rescue!
$ awk 'BEGIN{RS=ORS="\n}"}
/section2/{sub("host =[^\n]*", "host = \"newvalue\"")}NF' file
section1 {
...
setting1 = "true"
...
host = "localhost"
...
}
section2 {
...
host = "newvalue"
...
}
define the record structure, find the corresponding record and substitute.
Upvotes: 1
Reputation: 437082
Joao Morais's answer is probably the best choice.
To complement it with an awk
solution (a corrected, more robust version of karakfa's answer):
awk
: Thanks, @EdMortonawk '
BEGIN{ RS=ORS="}\n" }
/^section2/ { $0 =gensub(/(\n *host = )[^\n]*/, "\\1\"new value\"", 1) }
1' file
mawk
(less robust; also works with GNU awk
):awk '
BEGIN{ RS=ORS="}\n" }
/^section2/ { sub(/ host = [^\n]*/, " host = \"new value\"") }
1' file
Note: Due to use of a multi-character RS
value, neither solution works with BSD/OSX awk
; making it work there would require more effort.
Explanation:
RS=ORS="}\n"
tells awk
to split the input into records by }\n
instances (via special variable RS
, the input record separator), and to use the same separator for output (via ORS
, the output record separator).
/^section2/
matches literal section
at the beginning of each record.
Mawk solution: { sub(/ host = [^\n]*/, " host = \"new value\"") }
replaces the value for key host
with the new value.
([^\n]*)
, because just .*
would match to the end of the record, across line breaks.host
key more robustly, such as withsub("/(\n *host = )...
), but you'd need capture-group references to retain the same amount of preceding whitespace in the replacement value, which sub()
doesn't support.awk
, however, does offer capture-group references via its nonstandard gensub()
function; see below.GNU Awk solution: Use of the nonstandard gensub()
function enables the use of capture groups (\\1
refers to the 1st captured group, ...), which makes the replacement both more robust and more convenient.
gensub()
, unlike sub()
doesn't modify the input string and instead returns the modified copy - hence the need to assign it to $0
.1
as the 3rd argument tells gensub()
to only replace 1 occurrence.1
is a common shorthand for simply printing the resulting record.
Upvotes: 0
Reputation: 1925
sed -i.bak '/^section2 {/,/^}/s/host .*/host = "newvalue"/' file
This will search between section2 {
and the next }
, changing all host =
occurences. GNU sed syntax, you should use eg sed -i '' ...
on OSX.
Upvotes: 3