3

Note: The following question is related to this post, but both the focus of the question and the format of the input variable are different (i.e., contents of a multi-line file). Thus, the correct solution is likely different as well.

It appears that replacing a keyword in a string with a multi-line variable via sed works as long as said variable contains explicit newline characters (\n).

$ target="foo \n bar \n baz"
$ echo "qux\nquux" > file
$ solution=$(cat file)
$ echo "$solution"
qux\nquux
$ echo $target | sed -e "s|bar|$solution|"
foo \n qux
quux \n baz

However, when I open the file file in a text editor and substitute the newline character with a linebreak, the replacement with sed fails.

# Newline character was manually replaced with linebreak in texteditor.
$ solution=$(cat file)
$ echo "$solution"
qux
quux
$ echo $target | sed -e "s|bar|$solution|"
sed: -e expression #1, char 9: unterminated `s' command

How do I have to change the sed-command to conduct the sought substitution when the input variable that does not have explicit newline characters?

Michael Gruenstaeudl
  • 1,609
  • 1
  • 17
  • 31
  • Don't use sed for anything other than basic text replacements, use awk or perl which accept variables as arguments and save yourself tons of trouble. – 123 Oct 24 '17 at 15:34
  • @123 Right, but the above text replacement appears fairly basic to me. The issue is that newline characters and linebreaks are handled differently, and I don't know how to circumvent this issue. – Michael Gruenstaeudl Oct 24 '17 at 15:39
  • well sed doesn't support literal newlines in replacement so obviously it isn't basic enough. – 123 Oct 24 '17 at 15:45

1 Answers1

4

Introduction/Setup

sed is not necessarily the appropriate tool for this particular job. Consider the below options -- the samples for each of which expect the following setup:

# Run this before testing any of the code below
source='bar'
target='This string contains a target: <bar>'
solution='This has
multiple lines
and /slashes/ in the text'

...and each of which will emit the following output:

This string contains a target: <This has
multiple lines
and /slashes/ in the text>

Note that with sed, you would need to choose a delimiter that isn't used for the expression (thus, with s/foo/bar/, neither foo nor bar can contain a /); the below answers both avoid this limitation.


Shell-Builtin Parameter Expansion

The shell can perform the relevant substitution with only built-in string manipulation functionality:

result=${target//$source/$solution}
echo "$result"

Perl One-Liners As A sed Alternative

For longer input strings where the shell's built-in matching is inappropriate, you might also consider a perl one-liner, as described in BashFAQ #21:

in="$source" out="$solution" perl -pe 's/\Q$ENV{"in"}/$ENV{"out"}/g' <<<"$target"
Charles Duffy
  • 280,126
  • 43
  • 390
  • 441