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' fileBut 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.
12 Answers
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'/" filewill 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 pajamasso 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
matchedthat is1(only) when we’re in aServer 'Test EF'stanza. - Set it to
1when we matchServer 'Test EF', … - … and set it to
0when we see any other line containingServer. - When
matchedis non-zero (i.e., when we’re in aServer 'Test EF'stanza), search each line foroption port '1234'and substituteoption port '9876'.
This is right up awk's ally:
awk '$1 ~ q"Test EF"q { $3 = " option port "q"9876"q } 1' \ RS= FS='\n' q="'" OFS='\n' ORS='\n\n'By setting RS to the empty string, awk reads a whole "record" at a time. By setting FS to new-line, every line is a field.
Output:
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'