The @
is only special to Perl, not sed, so you could simply use sed
in this case:
sed -i -e 's/password/p@ssw0rd/g' filename # GNU sed
sed -i '' -e 's/password/p@ssw0rd/g' filename # BSD sed
You should get in the habit of using single-quotes when passing code to commands from the shell, because then none of the code is modified by the shell before the command sees it. If you do that, then backslashing the at sign will work for Perl:
perl -pi -e 's/password/p\@ssw0rd/g' filename
You can also use single-quote as the delimiter in the substitution expression, which causes the contained expressions to be interpreted literally with no expansion; that's easier if you go back to double-quotes in the shell:
perl -pi -e "s'password'p@ssw0rd'g" filename
But if you might need to worry about shell expansion in there, you need to both quote the string of code in the shell with single quotes and use single quotes as the delimiter, which is awkward enough that you're probably better off just going with the backslash on the at sign:
perl -pi -e 's'\''password'\''p@ssw0rd'\''g' filename # POSIX shell
perl -pi -e $'s\'password\'p@ssw0rd\'g' filename # bash/ksh/zsh ANSI string
To answer your question in the comments, if you want to make the old and new strings dynamic arguments passed as envariables, they would be $ENV{varname}
rather than just $varname
in Perl, and you have to treat them differently. You no longer have to worry about interpolation on the replacement value (since you want exactly one level of it), but you do have to worry about special regex characters on the pattern side, which means you need to put \Q
...\E
around what you're looking for:
CURRENTVALUE=password NEWVALUE=p@ssw0rd perl -pi -e 's/\Q$ENV{CURRENTVALUE}\E/$ENV{NEWVALUE}/g' filename
... that is, unless you want the value of $CURRENTVALUE to be able to be a regex instead of just a literal string. But you have to pick one.