4

So far, I've been using "\"" to print a double quote:

$ x="abc def"
$ echo "x=\"$x\""
x="abc def"

However, it seems like that behavior is undefined?

For echo https://pubs.opengroup.org/onlinepubs/9699919799/utilities/echo.html (ignoring the extra specs for XSI-conformant systems), a backslash leads to outright undefined behavior:

A string to be written to standard output. If the first operand is -n, or if any of the operands contain a <backslash> character, the results are implementation-defined.

The page for printf https://pubs.opengroup.org/onlinepubs/9699919799/utilities/printf.html refers mostly to the File Format Notation https://pubs.opengroup.org/onlinepubs/9699919799/basedefs/V1_chap05.html#tag_05 which also doesn't support \".

How about the following two methods? Are they explicitly POSIX-compliant?

  • Concatenating ' and " strings

    $ x="abc def"
    $ printf x=%s\\n '"'"$x"'"'
    x="abc def"
    
  • Using the octal value for ":

    $ x="abc def"
    $ printf x=\\42%s\\42\\n "$x"
    x="abc def"
    

What are the alternatives?

finefoot
  • 3,060
  • 1
    How about: printf 'x="%s"\n' "$x" ? – QuartzCristal Oct 06 '22 at 02:19
  • Each shell has a manual, most of the time, that is the easiest to undertand description of what a shell does. That is the shell spec. What POSIX is is the mixing of many points of view about each issue at the same time. It becomes very confusing if you do not have a general mental structure of how the shell works before trying to hang each little detail into that structure. IMO, anyways. – QuartzCristal Oct 06 '22 at 03:30
  • But isn't "the shell" part of POSIX, too? There is sh which I thought defines the "POSIX shell". Is that wrong? (But I think I might understand what you meant about "understanding the shell first". See my comment below.) – finefoot Oct 06 '22 at 12:28

2 Answers2

4

Yes, that behavior (using \") is undefined in POSIX for the command echo, not everywhere and for everything. That is one of the reasons to use printf instead of echo. Please read Why is printf better than echo?.

Of course, a clarification is in order here.

A \" is implementation-defined only if that is an operand to echo.

How is a word definition important here? Because what we write on the command line is read, many times modified, and then given to an utility as operands. What we call command line arguments ("words") are transformed is several ways by the shell, and one of the last changes is "quote-removal". If after all of that, the resulting operand contains \", echo is entitled to do anything it believe its the "correct" action.

If the command line is echo '\"' or echo "\\\"" which converts to a \" operand, the result is implementation-defined.

Your example of x="abc def"; echo "x=\"$x\"" is not implementation-defined because the operand that echo receive is x="abc def". But that depends on the content of the variable x.

To avoid such possible problem you should use printf, which doesn't have any limitation as no matter what the operands are, the output is POSIX defined.

Thus: The usual solution to print double quotes is to place them in the format part of the printf command:

$ printf 'x="%s"\n' "$x"
x="abc def"

If you need to print single quotes, you must change the quoting, or use the common trick of "get out of quotes - place a single quote - start quoting again":

$ printf 'x='\''%s'\''\n' "$x"
x='abc def'

Or:

$ printf "x='%s'"'\n' "$x"
x='abc def'

Or, even better, switch the burden of single quotes to the double quoted string:

$ printf 'x=%s\n' "'$x'"
x='abc def'

But yes, as printf supports \ddd where ddd is an octal number, you can also use:

$ printf 'x=\042%s\042\n' "$x"
x="abc def"

All solutions above are valid POSIX constructs, choose one.

4

Consider the following command being run in the POSIX shell:

echo "abc"

According to the rules specified in 2.2.3 Double-Quotes, "abc" is a double-quote, so the double quote characters aren't literals, but the enclosing characters for the double-quote construct. So echo doesn't even receive "abc" as its first parameter value, but only abc.

Furthermore, the last paragraph of 2.2.3 Double-Quotes defines the behavior of a backslash occurring inside a double-quote: for " and some other characters, it "shall retain its special meaning as an escape character", which is defined in section 2.2.1 Escape Character. According to that section, the escape character preserves "the literal value of the following character".

This means that the very first example from the original question text above, which sparked the question in the first place, is totally fine:

$ x="abc def"
$ echo "x=\"$x\""
x="abc def"

The echo program will never even receive any backslash characters because the POSIX shell has already used it as an escape character. So it doesn't matter what the echo specs (or printf or any other program) define if a backslash appears in the parameter value, because there is none.

Of course, all the above is limited and valid only to the value of x set above (and some other limited values). If the value of x could become anything else, it might so happen that it contains a backslash \ after quote-removal, which is the last step when converting a shell command line argument ("word" in shell parlance) to an operand in POSIX parlance about utilities.

That puts what follows echo at risk of becoming implementation-defined; printf doesn't have this problem.

ilkkachu
  • 138,973
finefoot
  • 3,060
  • @rattlesnake, ...on a closer read, I concede that "a\nb" is in fact well-defined as being identical to 'a\nb'. I'd been under the impression that POSIX was silent on how a backslash is interpreted when not followed by one of the explicitly-handled characters in a double-quoted string, but that impression was wrong: it's explicit that the backslash has no special meaning inside double quotes in any other case. – Charles Duffy Oct 06 '22 at 15:17
  • Yes, the backslash has no special meaning to the shell inside either double quotes or single quotes, so 'a\nb' is exactly equal to "a\nb" (to the shell). But echo receives a backquote in one of the "operands" to it in echo 'a\nb', so, it is implementation-defined. The result is not mandated by the spec (outside of xsi) and could mean anything the implementation chooses it to mean. Better off avoiding it, correct? @CharlesDuffy – QuartzCristal Oct 06 '22 at 15:27
  • @QuartzCristal, agreed fully. – Charles Duffy Oct 06 '22 at 15:31