1

I have the following content in a file

  dhcp_option_domain:
  - test.domain

And what I need to do is this:

whenever the value 'dhcp_option_domain:' followed by a newline and then ANY string, replace it with 'dhcp_option_domain:' followed by a newline and a variable.

ie if I set a variable of dhcp_domain="different.com" then then string above would convert to:

  dhcp_option_domain:
  - different.com

Note that both lines have and need to maintain leading 2 spaces.

I do not want to just do a search and replace on 'test.domain' as I have a few cases to use this and the values could be different each time the sed command is run.

I have tried a few methods such as:

dhcp_domain="something.com"
sed -i 's|dhcp_option_domain:\n.*|dhcp_option_domain:\n  - $dhcp_domain|g' filename

however cannot get it to work. Thanks.

xit
  • 103
  • 1
  • 10

3 Answers3

2

As the manual explains:

sed operates by performing the following cycle on each line of input: first, sed reads one line from the input stream, removes any trailing newline, and places it in the pattern space. Then commands are executed

Your regex (dhcp_option_domain:\n.*) does not match because there is no \n in the pattern space in the first place.

A possible solution:

sed '/dhcp_option_domain:$/{n;c\
  - '"$dhcp_domain"'
}'

The /dhcp_option_domain:$/ part is an address. The following command is only executed on lines matching that pattern.

The { } command groups multiple commands into a single block.

The n command prints out the current pattern space and replaces it by the next line of input.

The c\ command replaces the current pattern space by whatever follows in the script. Here it gets a bit tricky. We have:

  • a literal newline in the sed program (required after c\), then
  •   -  (placing those characters in the pattern space literally, then
  • ' (part of shell syntax, terminating the single-quoted part started by sed '...), then
  • " (starting a double-quoted part), then
  • $dhcp_domain (which, because it's in a double-quoted part, interpolates the contents of the dhcp_domain shell variable), then
  • " (terminating the double-quoted part), then
  • ' (starting another single-quoted part), then
  • a literal newline again (terminating the text after c\), then
  • } (closing the block started by {).
melpomene
  • 79,257
  • 6
  • 70
  • 127
  • Sed has an `-e` option, which adds a newline to `a`, `i`, `r`, `R`, `w`, `W` and `c` commands, so `sed -e '/dhcp_option_domain:$/{n;c\ - '"${var}" -e '}'` would work too. Another way to step around this problem is to negate the search e.g. `sed ' /dhcp_option_domain:$/!b;n;c\ -'"${var}"` – potong Aug 04 '18 at 07:13
  • @potong The [manual](https://www.gnu.org/software/sed/manual/sed.html#Other-Commands) said "*As a GNU extension, the c command and text can be separated into two -e parameters, enabling easier scripting*" and I wanted to keep it portable. – melpomene Aug 04 '18 at 08:36
  • I am probably wrong, but I have a feeling that the *new* GNU manual errs on the side of caution. The `-e` option is not GNU specific (see [here](http://pubs.opengroup.org/onlinepubs/009695399/utilities/sed.html)) and I believe it has always had this property. However I only use GNU sed and cannot verify this. – potong Aug 04 '18 at 09:23
1

By default, sed works line by line (using newline character to distinguish newlines)

$ cat ip.txt
foo baz
  dhcp_option_domain:
  - test.domain
123
  dhcp_option_domain:
$ dhcp_domain='something.com'
$ sed '/^  dhcp_option_domain:/{n; s/.*/  - '"$dhcp_domain"'/}' ip.txt
foo baz
  dhcp_option_domain:
  - something.com
123
  dhcp_option_domain:
  • /^ dhcp_option_domain:/ condition to match
    • {} to group more than one command to be executed when this condition is satisfied
  • n get next line
  • s/.*/ - '"$dhcp_domain"'/ replace it as required - note that shell variables won't be expanded inside single quotes, see sed substitution with bash variables for details
  • note that last line in the file didn't trigger the change as there was no further line
  • tested on GNU sed, syntax might vary for other implementations

From GNU sed manual

n

If auto-print is not disabled, print the pattern space, then, regardless, replace the pattern space with the next line of input. If there is no more input then sed exits without processing any more commands.

Sundeep
  • 19,273
  • 2
  • 19
  • 42
  • thanks that works perfectly on CentOS7.5 AND I don't need to requote the first string (dhcp_option_domain:) which is what I have been doing previously. What part of the code reuses or preserves the first string? You also answered my question within 30 mins which is astounding! – xit Aug 04 '18 at 04:15
  • 1
    check the quoted text from manual at end of answer... `If auto-print is not disabled, print the pattern space`.. in this case, on seeing `n`, sed will print the `dhcp_option_domain` line as is, and we will have only the next line to work with.. on GNU sed, using `N` instead of `n` will give you both lines to work with.. see [my tutorial](https://github.com/learnbyexample/Command-line-text-processing/blob/master/gnu_sed.md#n-and-n-commands) if you need more examples to understand.. – Sundeep Aug 04 '18 at 04:57
0

This might work for you (GNU sed):

sed '/dhcp_option_domain:$/{p;s//  - '"${var}"'/;n;d}' file

Match on dhcp_option_domain:, print it, substitute the new domain name (maintaining indent), print the current line and fetch the next (n) and delete it.

potong
  • 47,186
  • 6
  • 43
  • 72