"$@" expands to all of the command line arguments as separate words. It is different from "$*", which expands to all of the arguments as a single word.
"$@" is especially useful for looping through arguments and handling arguments with spaces.
Consider we are in a script that we invoked with two arguments, like so:
$ ./script.sh "␣1␣2␣" "␣3␣␣4␣"
The variables $* or $@ will expand into $1␣$2, which in turn expand into 1␣2␣3␣4 so the loop below:
for var in $*; do # same for var in $@; do
echo \<"$var"\>
done
will print for both
<1>
<2>
<3>
<4>
While "$*" will be expanded into "$1␣$2" which will in turn expand into "␣1␣2␣␣␣3␣␣4␣" and so the loop:
for var in "$*"; do
echo \<"$var"\>
done
will only invoke echo once and will print
<␣1␣2␣␣␣3␣␣4␣>
And finally "$@" will expand into "$1" "$2", which will expand into "␣1␣2␣" "␣3␣␣4␣" and so the loop
for var in "$@"; do
echo \<"$var"\>
done
will print
<␣1␣2␣>
<␣3␣␣4␣>
thereby preserving both the internal spacing in the arguments and the arguments separation. Note that the construction for var in "$@"; do ... is so common and idiomatic that it is the default for a for loop and can be shortened to for var; do ....