Usually double quotes in Bash mean "do everything between quotes in one word, even if it has delimiters". But, as you have noticed, $@ behaves differently when in double quotes. This is actually parsing that goes back to Bash's predecessor, the Bourne shell, and this particular behavior only applies to this particular variable.
Without this hack (I use this term because it seems inconsistent in terms of language, although it is very useful), it will be difficult for the shell of the script to pass an array of arguments to any other command that wants the same arguments. Some of these arguments may contain spaces in them, but how do you pass them to another command without a shell, either combining them as one big word, or rewriting the list and separating the arguments with a space?
Well, you can pass an array of arguments, and the Bourne shell really only has one array, represented by $@ , the number of elements of which is $# and whose elements are $1 , $2 , etc., the so-called positional parameters.
Example. Suppose you have three files in the current directory named one , two and thuh ree (the third file has a space in the name). You can initialize the array (that is, you can set the positional parameters) as file names in the current directory, for example:
set
Now the $@ array contains the file names, and this loop:
for FILE in " $@ "; do echo "$FILE" done
will produce this result:
one two thuh ree
because $ 1 = "one", $ 2 = "two", and $ 3 = "thuh ree" and " $@ " leaves the elements intact. Thanks, parsing hacking. If you leave quotes around $@ , the shell will smooth and re-parse the array, so you get this output:
one two thuh ree
In Bourne, this behavior does not apply to other variables that are not arrays. But Bash allows you to create other arrays, and even allows you to apply the old parsing to them using the special syntax "${ARRAYNAME[@]}" , whose at sign almost resembles Mr. Bourne.
Oh, and about your last example, what should the shell do with "pre $@ post" , where the decomposition takes place inside the word? Recent versions of Bash save the array and also add text before $@ to the first element of the array and add text to the last element:
pre one two thuh ree post