Replace the first occurence of a pattern in a file that may contain a slash

While:

sed "0,\~$var~s~$var~replacement~"

Can be used to change the regex delimiter, embedding variable expansions inside sed (or any other interpreter) code is a very unwise thing to do in the general case.

First, here, the delimiter is not the only character that needs to be escaped. All the regular expression operators need to as well.

But more importantly, and especially with GNU sed, that's a command injection vulnerability. If the content of $var is not under your control, it's just as bad as passing arbitrary data to eval.

Try for instance:

$ var='^~s/.*/uname/e;#'
$ echo | sed "0,\~$var~s~$var~replacement~"
Linux

The uname command was run, thankfully a harmless one... this time.

Non-GNU sed implementations can't run arbitrary commands, but can overwrite any file (with the w command), which is virtually as bad.

A more correct way is to escape the problematic characters in $var first:

NL='
'
case $var in
  (*"$NL"*)
    echo >&2 "Sorry, can't handle variables with newline characters"
    exit 1
esac
escaped_var=$(printf '%s\n' "$var" | sed 's:[][\/.^$*]:\\&:g')
# and then:
sed "0,/$escaped_var/s/$escaped_var/replacement/" < file

Another approach is to use perl:

var=$var perl -pe 's/\Q$ENV{var}\E/replacement/g && $n++ unless $n' < file

Note that we're not expanding the content of $var inside the code passed to perl (which would be another command injection vulnerability), but are letting perl expand its content as part of its regexp processing (and within \Q...\E which means regexp operators are not treated specially).

If $var contains newline characters, that may only match if there's only one at the end. Alternatively, one may pass the -0777 option so the input be processed as a single record instead of line-by-line.