9

Are

x=$y

and

x="$y"

always equivalent? Didn't know how to search for this. So far, I've always been using x="$y" to be "on the safe side". But I used x=$1 at one point and noticed that, apparently, I don't even need the extra double quotation marks.

Where is the behavior defined in The Open Group POSIX document?

finefoot
  • 3,060
  • 3
    This case is mentioned in When is double-quoting necessary? (under subheading Where you can omit the double quotes) but I don't think it provides a primary reference – steeldriver Oct 04 '22 at 23:51
  • 3
    section 2.9.1: assignments are subject to "tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal" but not field splitting and pathname expansion (aka globbing) which are exactly the ones double-quoting of a parameter expansion (or command substitution) prevents (tilde is never subject to other expansions, and arithmetic effectively can't be). – dave_thompson_085 Oct 05 '22 at 02:14
  • 2
    @dave_thompson_085 You posted this as comment, but it feels like a valid answer to me – Philippos Oct 05 '22 at 06:33

2 Answers2

11

Yes, x=$y and x="$y" are guaranteed to be the same in a POSIX shell. If you or some other reader of your code are unsure where double quotes must be used (see When is double-quoting necessary?), including the double quotes may be the safer option, as to not introduce confusion.

From the POSIX specification (section 2.9.1, "Simple Commands"):

When a given simple command is required to be executed [...], the following expansions, assignments, and redirections shall all be performed from the beginning of the command text to the end:

  1. The words that are recognized as variable assignments or redirections according to Shell Grammar Rules are saved for processing in steps 3 and 4.

[...]

  1. Redirections shall be performed as described in Redirection.
  2. Each variable assignment shall be expanded for tilde expansion, parameter expansion, command substitution, arithmetic expansion, and quote removal prior to assigning the value.

Note that the fourth point does not include field splitting or pathname expansion ("globbing"), which are usually part of the expansions done when the word is not recognised as a variable assignment. Since these steps are removed for assignments, the quoting is not necessary.

See also section 2.6, "Word Expansions":

The order of word expansion shall be as follows:

  1. Tilde expansion (see Tilde Expansion), parameter expansion (see Parameter Expansion), command substitution (see Command Substitution), and arithmetic expansion (see Arithmetic Expansion) shall be performed, beginning to end. See item 5 in Token Recognition.
  2. Field splitting (see Field Splitting) shall be performed on the portions of the fields generated by step 1, unless IFS is null.
  3. Pathname expansion (see Pathname Expansion) shall be performed, unless set -f is in effect.
  4. Quote removal (see Quote Removal) shall always be performed last.

The following only matters if y in x=$y and x="$y" is actually one of the special parameters * or @:

Note that since "$@" expands to a list of strings, it is unspecified what x=$@ and x="$@" do, while x=$* is the same as x="$*". In some shells (e.g. bash, ksh93), using $@ like this is the same as using $* when the first character of $IFS is a space, while in others (e.g. busybox sh, dash, zsh) it's the same as $* and uses the first character of the set value of $IFS.

Kusalananda
  • 333,661
  • 1
    True as long as y is not * nor @ (only x="$*" is specified among x=$@ x="$@" x=$* x="$*", and in practice you see some variations between shells for the other ones). – Stéphane Chazelas Oct 05 '22 at 07:21
  • @StéphaneChazelas I can't seem to find where it says x=$* is not the same as x="$*". Field splitting is not happening, so they should be the same. – Kusalananda Oct 05 '22 at 07:37
  • You're right. I may be misremembering the resolution of https://www.austingroupbugs.net/view.php?id=888 – Stéphane Chazelas Oct 05 '22 at 07:43
  • iirc there was some Bash bug with x=$* without the quotes, so one might suggest adding the quotes anyway to guard against hitting that – ilkkachu Oct 05 '22 at 08:14
  • @ilkkachu Thanks for the edit. One question: Should it say $IFS instead of $* at the very end? (EDIT: I think it should be $IFS). – Kusalananda Oct 05 '22 at 08:21
  • 1
    @Kusalananda, argh yes, my bad. The actual set value of $IFS of course (as opposed to implicitly using a space). – ilkkachu Oct 05 '22 at 14:31
1

In general, there are two parts where a variable could be defined.

  • Leading variable assignments

    As described in the dash manual for simple commands:

    Leading words of the form "name=value" are stripped off ...

    In those words, it is true that x=$y is exactly equal to x="$y".

    In such words is that the spirit of section 2.9.1, "Simple Commands" is followed and there is no word splitting nor pathname expansion applied. As those two expansions are the only ones that could generate new words, there is no possibility of splitting a variable containing IFS characters, or matching several file names. There, a word x=$y will remain as one single word.

  • Rest of arguments

    After a word which stands as the "command name" is found, the parsing changes and the assignments may get split and thus become something else.

    A bash manual description for this is:

    Assignment statements may also appear as arguments to the alias, declare, typeset, export, readonly, and local builtin commands (declaration commands).

    And there is where quoting is needed. Similar to declare x="$y".

    Of course, in POSIX, only export and readonly have real meaning.

    And only fails in yash nowadays:

    $ yash -c 'unset x; y="one two"; readonly x=$y; echo "x=$x"'
    x=one
    

So, yes, in general, x=$y and x="$y" are completely equivalent except in some cases where the variable assignment exists after a command name.