Find and replace text in a file after match of pattern only for first occurrence using sed
Let's say I have the following input file:
Server 'Test AB'
option type 'ss'
option port '1234'
option timeout '60'
Server 'Test CD'
option type 'ss'
option port '1234'
option timeout '60'
Server 'Test EF'
option type 'ss'
option port '1234'
option timeout '60'
Server 'Test GH'
option type 'ss'
option port '1234'
option timeout '60'
And now I want to use sed to exchange "option port '1234'" with "option port '9876'", but I only want to do this for Server 'Test EF'. All the other ports should stay unchanged.
Output should look like:
Server 'Test AB'
option type 'ss'
option port '1234'
option timeout '60'
Server 'Test CD'
option type 'ss'
option port '1234'
option timeout '60'
Server 'Test EF'
option type 'ss'
option port '9876'
option timeout '60'
Server 'Test GH'
option type 'ss'
option port '1234'
option timeout '60'
I already tried many sed commands, I found out that I can search between lines with
sed '11,15s/port '1234'/port '9876'/g' file
But this doesn't help me, because Server 'Test EF' won't be always in the same lines. I would need to limit the area instead of 11,15 with for example "'Test EF',option timeout" but no idea how I can realize it.
Solution 1:
Contrary to what the previous answer says,
this is right up sed
’s alley.
And, contrary to another suggestion (now deleted),
it is easily done in a single sed
process.
sed "/Server 'Test EF'/,/Server/ s/option port '1234'/option port '9876'/" file
will do what you want.
Some say sed
code is usually quite cryptic,
unless you're a sed
expert.
While it may be hard to write,
I believe that it’s fairly easy to read and understand:
- Starting at a line that contains
Server 'Test EF'
, - and continuing through the next line that contains
Server
, - search each line for
option port '1234'
- and, if you find it, substitute
option port '9876'
.
You were actually fairly close.
Quoting was a big problem with your attempt:
you can’t use single-quote characters ('
) in a string
that’s also delimited with single-quotes
(i.e., that begins and ends with single-quotes).
One way to handle this (which my answer, above, uses)
is to enclose the string
in double-quote characters ("
); as in
… "… … s/option port '1234'/option port '9876'/" …
↑ ↑
That works fine if the string doesn’t contain
any "
characters — or $
, \
or `
.
If you’re using bash (as you indicate with your tags),
you can use $'……\'……'
; e.g.,
echo $'the cat\'s pajamas'
the cat's pajamas
so you could do
… $'… … s/option port \'1234\'/option port \'9876\'/' …
⇑⇑ ⇑⇑ ⇑⇑ ⇑⇑
And here’s a much-less-cryptic way of doing it in awk:
awk $'
/Server/ { matched=0 }
/Server \'Test EF\'/ { matched=1 }
matched { gsub("option port \'1234\'", "option port \'9876\'") }
{ print }
'
- Define a variable called
matched
that is1
(only) when we’re in aServer 'Test EF'
stanza. - Set it to
1
when we matchServer 'Test EF'
, … - … and set it to
0
when we see any other line containingServer
. - When
matched
is non-zero (i.e., when we’re in aServer 'Test EF'
stanza), search each line foroption port '1234'
and substituteoption port '9876'
.