sed
processes the input line by line, so you can't match a multiline string.
You can use Perl which can read the whole file into memory (that's what the -0777
does).
perl -0777 -pi -e "s/$file_contents/$escaped_replacement/g" "$file"
This can still break if the file contains a slash. You can prevent this by using an ENV variable instead of expanding the shell variable in the command before Perl sees it:
fc=$file_contents perl -0777 -pi -e "s/\$ENV{fc}/$escaped_replacement/g" "$file"
(and probably do the same with the other variable, then you can use single quotes for the expression, see below).
If the file contains special regex constructs (e.g. {1,3}
) and you want to replace them literally, you might need to precede the pattern with a \Q
. This will call quotemeta to escape all the special characters for you.
fc=$file_contents er=$escaped_replacement perl -0777 -pi -e \
's/\Q$ENV{fc}/$ENV{er}/g' "$file"
If you insist on using sed
, here are some steps to try:
It seems you can prefix newlines with backslashes to prevent sed from complainig about syntax:
file_contents=${file_contents//$'\n'/$'\\\n'}
But you can't match a multiline string this way, even if the syntax was correct - unless you use GNU sed. It can load the whole file for you with -z
, similarly to Perl above, so if you use the substitution mentioned above, you can run
sed -i -z "s=$file_contents=$escaped_replacement=" "$file"
You might also need to do more substitutions if the file contains slashes, backslashes, etc. For example,
file_contents=${file_contents//'\'/'\\'} # Escape backslashes.
file_contents=${file_contents//'/'/'\/'} # Escape slashes.