I would like to put this command into a file to be run later:

ln -s "$xr"/ya.txt ~

I can do that with (1):

cat > zu.sh <<eof
ln -s "$xr"/ya.txt ~

or (2):

printf 'ln -s "%s"/ya.txt ~\n' "$xr" > zu.sh

or (3):

echo "ln -s '$xr'/ya.txt ~" > zu.sh

or (4):

printf 'ln -s %q/ya.txt ~\n' "$xr" > zu.sh

or (5):

printf 'ln -s "%s"/ya.txt ~\n' "${xr//\"/\\\"}"

however each solution is problematic. if the variable in (1) or (2) contains a double quote, they will fail; if the variable in (3) contains a single quote, it will fail. (4) is good but is not defined by POSIX. (5) is good but is a Bashism. it looks like the best option would be to use (1) or (2) and escape any double quotes in the variable, but can that be done another way?


1 Answer 1


I think this is safe:

esc() {
    printf "%s\n" "$1" | sed -e "s/'/'\"'\"'/g" -e "1s/^/'/" -e "\$s/\$/'/"

It single-quotes the string, so that any $, `, \, and " in the input string don't matter, and turns any existing ' characters into '"'"' (i.e. end single-quoting, double-quote a lone single-quote, then reënter single-quoting).

It was tempting to use $(...) command substitution in there, except that then it will eat any trailing newlines in the input. Instead the opening and closing quotes are inserted by the second and third sed scripts themselves, at the start of the first line and the end of the last line. Any embedded newlines are left raw, which is fine.

The output is suitable for copying back into the shell, even in the most pathological case I can invent (using Bash ANSI-C quoting $'...' to create the test string in the first place, but not afterwards):

bash-4.4$ esc $'abc\ndef ghi\'jkl$mno`pqr\\stu\\\'vwx\n\n\n'
def ghi'"'"'jkl$mno`pqr\stu\'"'"'vwx

bash-4.4$ echo 'abc
> def ghi'"'"'jkl$mno`pqr\stu\'"'"'vwx
> '
def ghi'jkl$mno`pqr\stu\'vwx

bash-4.4$ dash
$ echo 'abc
def ghi'"'"'jkl$mno`pqr\stu\'"'"'vwx

'> > > >
def ghi'jkl$mno`pqr\stu\'vwx


It's safe to put that into a variable xr=$(esc "$xr") and then use that in ordinary substitution later on within your here-document or elsewhere.

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .