0

I'm trying to replace a string inside a text file using sed. This string could contain any special character like ., /, $, etc. However, I'm interested to replace such a string literally, that is to not interpret ., /, $, etc. as special characters.

So, the simple instruction

sed -i s|$old|$new| "$path_to_file"

doesn't work as I expect if I want to replace 20.8 with 20.9 for example.

Of course, before starting the script that uses this sed, I don't have any idea where the special characters will be placed by the user.

Is there a way to do this with sed or any other tools (perl, awk, etc.)?

Thanks!

1 Answers1

0

If file does not contain nulls and will fit comfortably in memory, you can just use bash parameter expansion directly:

data=$(<"$path_to_file")
echo "${data//"$old"/"$new"}" >"$path_to_file"

Don't forget the double-quotes, they disable special pattern characters.


Notes:

  1. date=$(...) strips trailing newlines; use mapfile to keep them
  2. echo can produce unintended output in some corner conditions; use printf to avoid them

So, slightly longer but much improved version:

mapfile -d '' data <"$path_to_file"
printf '%s' "${data//"$old"/"$new"}" >"$path_to_file"
jhnc
  • 11,310
  • 1
  • 9
  • 26
  • Thank you for refining your (already) excellent answer. Could you tell me why are you using the `-d ''` option? – user9952796 Jun 27 '23 at 10:01
  • 1
    consider: `old=$'\nb'; new='-'; printf 'a\nbc\nd' >f; mapfile a – jhnc Jun 27 '23 at 10:33
  • 1
    Basically, you want everything stuffed into the first array element otherwise matching can fail if `$old` includes the delmiter. And ignoring that you'd have to use `"${data[*]//...}"` or somesuch. – jhnc Jun 27 '23 at 10:42