5

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 ~ eof 

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?

2
  • 1
    Contains a double quote, or a backslash, or a dollar sign, or a backtick, or ... Commented Feb 6, 2018 at 1:48
  • 1
    See also those answers to related questions. 4 and 5 are not safe. Commented Feb 8, 2018 at 16:05

1 Answer 1

4

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' 'abc def ghi'"'"'jkl$mno`pqr\stu\'"'"'vwx ' bash-4.4$ echo 'abc > def ghi'"'"'jkl$mno`pqr\stu\'"'"'vwx > > > ' abc def ghi'jkl$mno`pqr\stu\'vwx bash-4.4$ dash $ echo 'abc def ghi'"'"'jkl$mno`pqr\stu\'"'"'vwx '> > > > abc 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.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.