Shell – Quote String with Single Quotes Instead of Backslashes

bashposixquotingshellzsh

How can I quote a string with single quotes?

Eg, I can do:

$ printf "%q\n" 'two words'
two\ words
$

Is there a way to get a single- (or double-) quoted string as output, ie:

$ MAGIC 'two words'
'two words'
$

I find the single-quoted version much easier to read.

I'd like an answer which works for {ba,z}sh. POSIX shell would be a bonus.

Best Answer

Assuming that:

$ value=$'This isn\'t a \n\x1b "correct" test'
$ printf '%s\n' "$value"
This isn't a
"correct" test

quote () { printf %s\\n "$1" | sed "s/'/'\\\\''/g;1s/^/'/;\$s/\$/'/" ; }

Use:

$ quote "${value}"
'This isn'\''t a
"correct" test'

From Rich's sh posix tricks

This function simply replaces every instance of «'» (single quote) within the string with «'\''» (single quote, backslash, single quote, single quote), then puts single quotes at the beginning and end of the string. Since the only character whose meaning is special within single quotes is the single quote character itself, this is totally safe. Trailing newlines are handled correctly, and the single quote at the end doubles as a safety character to prevent command substitution from clobbering the trailing newlines, should one want to do something like:

 quoted=$(quote "$var")

Warning: the ESC (\033 or \x1b or decimal 27) characters above gets (technically) quoted, but is invisible. When sent to a terminal, like other control characters, could even do harm. Only when they are visually presented as $'\033', $'\C-[' or $'\E', they are clearly visible and unambiguous.

printf '%s\n' "${value@Q}" $'This isn\'t a \n\E "correct" test'

printf '%s\n' ${(q)value} This\ isn\'t\ a\ $'\n'$'\033'\ \"correct\"\ test
printf '%s\n' ${(qq)value} 'This isn'\''t a "correct" test'
printf '%s\n' ${(qqq)value} "This isn't a \"correct\" test"
printf '%s\n' ${(qqqq)value} $'This isn\'t a \n\033 "correct" test'
printf '%s\n' ${(q-)value} 'This isn'\''t a "correct" test'
printf '%s\n' ${(q+)value} $'This isn\'t a \n\C-[ "correct" test'

Be careful with some zsh quoted strings: the ESC (\033 or \x1b or decimal 27) characters above are all (technically) quoted, but invisible. When sent to a terminal, like other control characters, could even do harm. Only when they are visually presented as $'\033', $'\C-[' or $'\E', they are clearly visible and unambiguous.

From Bash's manual:

${parameter@operator}
Q The expansion is a string that is the value of parameter quoted in a format that can be reused as input.

From the zshexpn man page:

q
Quote characters that are special to the shell in the resulting words with backslashes; unprintable or invalid characters are quoted using the $'\NNN' form, with separate quotes for each octet.

If this flag is given twice, the resulting words are quoted in single quotes and if it is given three times, the words are quoted in double quotes; in these forms no special handling of unprintable or invalid characters is attempted. If the flag is given four times, the words are quoted in single quotes preceded by a $. Note that in all three of these forms quoting is done unconditionally, even if this does not change the way the resulting string would be interpreted by the shell.

If a q- is given (only a single q may appear), a minimal form of single quoting is used that only quotes the string if needed to protect special characters. Typically this form gives the most readable output.

If a q+ is given, an extended form of minimal quoting is used that causes unprintable characters to be rendered using $'...'. This quoting is similar to that used by the output of values by the typeset family of commands.