You can't do this job easily and robustly with sed, see Is it possible to escape regex metacharacters reliably with sed. Instead just use a tool like awk that understands literal strings:
$ string='~`!@#$%^&*()-_+={[}]|\:;"'\''<,>.?/\1'
$ echo "$string"
~`!@#$%^&*()-_+={[}]|\:;"'<,>.?/\1
$ string="$string" awk -i inplace 'match($0,/(.*)(<<string>>)(.*)/,a){ $0=a[1] ENVIRON["string"] a[3] } 1' file.txt
$ cat file.txt
My string: ~`!@#$%^&*()-_+={[}]|\:;"'<,>.?/\1
That above will work for any characters (or backreference substrings like \1
) that string
might contain because it's simply using a literal string operation (concatenation) for the replacement.
It's using GNU awk for -i inplace
just the same as your original script used GNU sed for -i
.
Don't use all upper case for non-exported variable names by the way to avoid clashes with exported and built-in variables and not obfuscate your code by making it look like you're using exported variables, see Correct Bash and shell script variable capitalization.
Note that if you have multiple <<whatever>>
placeholders you can easily parameterize the above, e.g.:
$ foo='Now is the Winter'
$ bar='Of our discontent'
$ cat file.txt
My foo string: <<foo>>
My bar string: <<bar>>
$ foo="$foo" bar="$bar" awk -i inplace 'match($0,/(.*)<<(\w+)>>(.*)/,a) && (a[2] in ENVIRON){ $0=a[1] ENVIRON[a[2]] a[3] } 1' file.txt
$ cat file.txt
My foo string: Now is the Winter
My bar string: Of our discontent
If you don't want to set foo
and bar
on the awk command line you can export them before it, or read them from a config file or a here-doc or ... - lots of options.