AskMath
AskMath

Reputation: 425

What is the difference between ${@:2} and ${*:2} in these cases?

Given the following code in bash:

filename=${1} 
content1=${@:2}
content2="${@:2}"
content3=${*:2}
content4="${*:2}"
echo  "${content1}" > "${filename}"
echo   "${content2}" >> "${filename}"
echo  "${content3}" >> "${filename}"
echo   "${content4}" >> "${filename}"

What is the difference between the "contents" ? When I will see the difference? What is the better way to save the content that I get and why?

Upvotes: 0

Views: 242

Answers (2)

Benjamin W.
Benjamin W.

Reputation: 52112

In an assignment, the right-hand side doesn't have to be quoted to prevent word splitting and globbing, unless it contains a blank.

These two are the same:

myvar=$var
myvar="$var"

but these are not:

myvar='has space'
myvar=has space

The last one tries to run a command space with the environment variable myvar set to value has.

This means that content1 is the same as content2, and content3 is the same as content4, as they only differ in RHS quoting.

The differences thus boil down to the difference between $@ and $*; the fact that subarrays are used doesn't matter. Quoting from the manual:

(for $*):

When the expansion occurs within double quotes, it expands to a single word with the value of each parameter separated by the first character of the IFS special variable.

(for $@):

When the expansion occurs within double quotes, each parameter expands to a separate word.

Since you're using quotes (as you almost always should when using $@ or $*), the difference is that "${*:2}" is a single string, separated by the first character of IFS, and "${@:2}" expands to separate words, blank separated.

Example:

$ set -- par1 par2 par3      # Set $1, $2, and $3
$ printf '<%s>\n' "${@:2}"   # Separate words
<par2>
<par3>
$ printf '<%s>\n' "${*:2}"   # Single word, blank separated (default IFS)
<par2 par3>
$ IFS=,                      # Change IFS
$ printf '<%s>\n' "${*:2}"   # Single word, comma separated (new IFS)
<par2,par3>

As for when to use $@ vs. $*: as a rule of thumb, you almost always want "$@" and almost never the unquoted version of either, as the latter is subject to word splitting and globbing.

"$*" is useful if you want to join array elements into a single string as in the example, but the most common case, I guess, is iterating over positional parameters in a script or function, and that's what "$@" is for. See also this Bash Pitfall.

Upvotes: 2

chepner
chepner

Reputation: 530940

The only difference is that using $* will cause the arguments to be concatenated with the first character of IFS, while $@ will cause the arguments to be concatenated with a single space (regardless of the value of IFS):

$ set a b c
$ IFS=-
$ c1="$*"
$ c2="$@"
$ echo "$c1"
a-b-c
$ echo "$c2"
a b c

The quotes aren't particularly important here, as expansions on the right-hand side of an assignment aren't subject to word-splitting or pathname expansion; content1 and content2 should always be identical, as should be content3 and content4.

Which is better depends on what you want.

Upvotes: 1

Related Questions