TL;DR: skip to the conclusion.
While several shells/tools have builtin quoting operators some of which have already been mentioned in a few answers, I'd like to stress here that many are unsafe to use depending on:
in some contexts, it's important the empty string be represented as ''
or ""
. For instance, if it's to be used in sh -c "cmd $quoted_output"
it matters if we want what was quoted to be passed as one argument to cmd
. In sh -c "var=$quoted_output; ..."
, it doesn't matter whether the empty string is represented as ''
, ""
or as the empty string.
The $var:q
operator of zsh
represents the empty string as the empty string, not ''
, ""
nor $''
.
The ${var@Q}
operator of bash
(itself copied from mksh
which behaves differently in this regard), represents an empty $var
as ''
, but an unset $var
as the empty string:
$ empty_var= bash -c 'printf "<%s>\n" "${empty_var@Q}" "${unset_var@Q}"'
<''>
<>
$ empty_var= mksh -c 'printf "<%s>\n" "${empty_var@Q}" "${unset_var@Q}"'
<''>
<''>
$ empty_var= zsh -c 'printf "<%s>\n" "${empty_var:q}" "${unset_var:q}"'
<>
<>
some of those quoting operators will use a combination of '...'
, \
, "..."
or $'...'
. The syntax of the latter varies between shells and between versions of a given shell. So for those operators that do use it or can use it depending on the input, it's important that the result be used in the same shell (and same version thereof). That applies at least to:
- the
printf %q
of GNU printf
, bash
, ksh93
, zsh
zsh
's $var:q
, ${(q)var}
, ${(q+)var}
, ${(qqqq)var}
,
mksh
's ${var@Q}
bash
's ${var@Q}
,
- the
typeset
/declare
/export -p
output of ksh93
, mksh
, zsh
- the
alias
/set
output of bash
, ksh93
, mksh
, zsh
- the
xtrace
output of ksh93
, mksh
, zsh
In any case $'...'
is not (yet¹) a standard sh
quoting operator, and beware that non-Bourne-like shells such as rc
, es
, akanga
, fish
have completely different quoting syntax. There is simply no way to quote a string in a way that is compatible with every shell in existence (though see this other Q&A for some ways to work around it).
some shells decode their input as characters before interpreting the code in it, some don't, and some do it sometimes, and sometimes not.
Some shells (like bash
) also make their syntax conditional on the locale. For instance, token delimiters in the syntax are the characters considered as blanks in the locale in yash
and bash
(though in bash
, that only works for single-byte ones). Some shells also rely on the locale's character classification to decide what characters are valid in a variable name. So for instance Stéphane=1
could be interpreted as an assignment in one locale, or as the invocation of the Stéphane=1
command in another.
The sequence of bytes 0xa3 0x5c represents the £\
string in the ISO-8859-1 (aka latin1) character set, the α
character in BIG5, or an invalid sequence of bytes in UTF-8. \
happens to be a special character in the shell syntax, including within "..."
and $'...'
. `
is also a (dangerous) character whose encoding can be found in the encoding of other characters in some locales.
Byte 0xa0
is the non-breaking-space character in a great number of single-byte character sets and that character is considered as blank in some locales on some systems, and as such as a token delimiter in the syntax of bash
or yash
there.
That byte is also found in the UTF-8 encoding of thousands of characters including many alphabetical ones (like à
, encoded as 0xc3 0xa0).
I'm not aware of any charset in use in any locale of any ASCII-based systems that have characters whose encoding contains the encoding of '
though.
Some shell quoting operators output $'\u00e9'
or $'\u[e9]'
for the é
character for instance. And that in turn, when used, depending on the shell, and the locale at the time of interpreting or running the code that uses it will be expanded to its UTF-8 encoding or in the locale's encoding (with variation in behaviour if the locale doesn't have that character).
So, it's not only important that the resulting string be used in the same shell and shell version, but also that it be used in the same locale (at least for those shells that do some character encoding/decoding). And even then, several shells (including bash
) have or have had bugs in that regard.
Any quoting operator that uses $'...'
, "..."
, or backslash for quoting or that leaves some non-ASCII characters unquoted is potentially unsafe.
Or in other words, only the ones that use '...'
are safe in that regard. That leaves:
zsh
's ${(qq)var}
operator
- The
alias
output of dash
/bash
,bosh
(at least current versions).
- The
export -p
of dash
/bosh
(at least current versions).
- the
set
output of dash
(at least current versions).
Though of those only the first is documented and committed to always use single quotes (though note the caveat about rcquotes
below).
Also note that yash
can't cope with data that can't be decoded in the locale's charset, so there's no way to pass arbitrary data to that shell (at least in the current version).
Ironically, the output of the locale
utility has the problem (as it's required to use "..."
to output implied settings), and it's typically intended to be used to input code in a locale that is different from that where locale
was invoked (to restore the locale).
The NUL character (0 byte) cannot occur in an environment variable or in arguments of a command that is executed by way of the execve()
system call (that's a limitation of that system call that takes those env and arguments strings as C-style NUL-delimited strings). Except in zsh
, NUL cannot be found in shell variables or builtin arguments or more generally shell code either.
A 0 byte however can be read and written alright from/to a file or pipe or any I/O mechanism.
In zsh
it can be stored in a variable, read and written, passed as argument to builtins like in any modern programming language (such as python
or perl
).
But bear in mind that if you quote a NUL with any method that leaves it as-is (as opposed to $'\0'
, $'\x0'
, $'\u0000'
, $'\C@'
for instance), regardless of how it is quoted, the result cannot be passed in an argument or env var to an executed command, and no other shell will be able to make use of that NUL character.
That's possibly to bear in mind if you take external input in zsh
, as in IFS= read -r var
. If a NUL byte is included in that line read from stdin, $var
and ${(qq)var}
will contain it which may restrict what you can do with it.
That's one case where using the $'...'
form of quoting can be preferable (if the other caveats associated with that form of quoting (see above) can be addressed).
If the resulting quoted text is to be used in shell code located inside backticks, beware that there's an extra layer of backslash interpretation. Always use $(...)
in place of `...`
.
Some characters are only special in some context. For instance =
is special in the words that precede the command name (as in a=1 cmd arg
), but not after² (as in cmd a=1
), though there are some special cases in some shells for commands like export
, readonly
...
~
is special in some contexts and not others.
Not all quoting operators will quote those.
Some characters are special in some shells but not in others, or only when some option is enabled...
Even digits are special in some contexts. For instance sh -c "echo ${quoted_text}>file"
would not output the quoted text in file
, if 2
was not quoted as '2'
for instance.
The only quoting method that is safe (if we limit to Bourne-like shells and disregard yash
and `...`
or rogue locales, and assume the data doesn't contain NUL characters) is single quoting of everything (even the empty string, even characters you'd imagine never to be a problem), and represent the single quote character itself as \'
or "'"
outside of the single-quotes, as was the initial intent in your question.