1

I want to replace a placeholder on a file <<string>> in the example to the contents of a var that has several special characters.

file.txt

My string: <<string>>

script.sh

STRING="something-else;device=name.of.device;key=abcd1234/wtp="
sed -i "s/<<string>>/${STRING}/g" file.txt

I get this error:

sed: -e expression #1, char 165: unknown option to `s'

I already use this sed command for other vars that do not have special characters. Any way to escape the var ${STRING} entirely?

oguz ismail
  • 1
  • 16
  • 47
  • 69
  • See: https://unix.stackexchange.com/questions/129059/how-to-ensure-that-string-interpolated-into-sed-substitution-escapes-all-metac – virchau13 Jul 26 '21 at 12:54
  • Does this answer your question? [Escape a string for a sed replace pattern](https://stackoverflow.com/questions/407523/escape-a-string-for-a-sed-replace-pattern) – KamilCuk Jul 27 '21 at 14:29

2 Answers2

1

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.

Ed Morton
  • 188,023
  • 17
  • 78
  • 185
0

Since STRING contains a /, you should use a other delimiter, for example, you can use ^ like so:

sed  's^<<string>>^'"$STRING"'^g' file.txt

The quoting logic (''""'') is explained nicely on this SO answer.


Example on my locale machine:

$
$ cat file.txt
My string: <<string>>
$
$
$ STRING="something-else;device=name.of.device;key=abcd1234/wtp="
$
$
$ sed -i 's^<<string>>^'"$STRING"'^g' file.txt
$
$ cat file.txt
My string: something-else;device=name.of.device;key=abcd1234/wtp=
$
$
0stone0
  • 34,288
  • 4
  • 39
  • 64
  • or skip the `''""''` and use double quote for the entire script: `sed "s^<>^$STRING^g" file.txt` – markp-fuso Jul 26 '21 at 13:28
  • The script in the answer would fail if `STRING` contained `^`, or `&`, or `\1` (and some less likely others such as a newline). – Ed Morton Jul 27 '21 at 13:52