0

The bash manual says:

Regarding: $*

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. That is, "$*"is equivalent to "$1c$2c...", where c is the first character of the value of the IFS variable.

Regarding: $@

When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" ....

Provided the first character of the value of the IFS variable is in fact a single space, I can't seem to come up with an example where these two special parameters would produce different behavior. Can anyone provide me with an example (again, without changing IFS) where they would produce different behavior?

My own test, which still baffles me a bit, is as follows:

#!/usr/bin/env bash # File: test.sh # set foo and bar in the global environment to $@ and $* test_expansion () { foo="$@" bar="$*" } 

Now testing:

. test.sh test_expansion a b c d # foo is $@ # bar is $* for e in "$foo"; do echo "$e" done # a b c d for e in "$bar"; do echo "$e" done # a b c d 
5
  • Did you read all the way through Mikel's answer? Did you read the first half of Wojtek Rzepala's answer? Most importantly, did you read Carlos Campderrós's answer?  It's probably the best of the bunch, and it doesn't mention IFS at all. Commented Nov 26, 2018 at 20:56
  • 1
    Yeah, Stack Exchange's search engine sucks.  Half of the time I want to search Stack Exchange, I use Google; see, for example, this search. … P.S. Gilles's answer, although a late addition, is pretty good, too.  (Gilles's answers usually are.) Commented Nov 26, 2018 at 21:10
  • 1
    The fact that it's an assignment in your test is the key here. Splitting the value on the right hand side of an assignment doesn't make much sense, since the assigned value can only be a single string anyway. So the fact that "$@" assigns to separate words doesn't apply there. That's discussed in Unexpected outcome of a=“$@”. Commented Nov 26, 2018 at 21:19
  • 1
    Right.  $@ is special.  Once you assign it to an ordinary variable, you lose that special quality.  But try using arrays: foo=("$@") / bar=("$*") / set | grep '^foo=' / set | grep '^bar='. Commented Nov 26, 2018 at 21:36
  • @Scott thanks. That entirely resolves things. It was somehow unsatisfying to think, oh, it just doesn't work when you assign it to a variable, which, I think added to the general opacity of bash. Commented Nov 26, 2018 at 21:42

3 Answers 3

3

The difference comes in when you have arguments you pass in on the command line with IFS characters in them (e.g. an argument with a space). To see the difference, look at this script:

#!/bin/bash echo 'Here is $*' for x in "$*"; do echo " !${x}!" done echo "" echo 'And here is $@' for x in "$@"; do echo " !${x}!" done exit 0 

Now, look at the difference when you pass in an argument with a space.

./testspace01.sh "This is" a test Here is $* !This is a test! And here is $@ !This is! !a! !test! 

UPDATE Ah, assigning what was passed in on the command line to a variable brings its own little quirks. :)

Remember, everything that was passed in on the command line is an array. So, assigning the array to a string gives you something different than signing to an array. And, handling the array is different depending on if you are using the star or the asterisk. Here is an updated version of my script.

#!/bin/bash s_star="$*" echo 'Here is s_star' for x in "${s_star}"; do echo " !${x}!" done a_star=("$*") echo "" echo 'Here is a_star' for x in "${a_star}"; do echo " !${x}!" done s_at="$@" echo "" echo 'Here is s_at' for x in "${s_at}"; do echo " !${x}!" done a_at=("$@") echo "" echo 'Here is a_at (using star)' for x in "${a_at[*]}"; do echo " !${x}!" done echo "" echo 'Here is a_at (using at)' for x in "${a_at[@]}"; do echo " !${x}!" done exit 0 

Here is the output:

./testspace02.sh "This is" a test Here is s_star !This is a test! Here is a_star !This is a test! Here is s_at !This is a test! Here is a_at (using star) !This is a test! Here is a_at (using at) !This is! !a! !test! 

As you can see, there is different behaviors.

2
  • ...or just printf '!%s!\n' "$*" vs printf '!%s!\n' "$@" Commented Nov 26, 2018 at 21:12
  • This acts as I expected it would on reading the documentation, but when I ran my own test (putting the values for "$@" and "$*" into a global variable and then manipulating them in an interactive session), it doesn't behave this way. Commented Nov 26, 2018 at 21:18
3

Try this:

#!/bin/bash show-difference () { for e in "$@" ; do printf '<%s>\n' "$e" done for e in "$*" ; do printf '[%s]\n' "$e" done } show-difference {a..h} 

Output:

<a> <b> <c> <d> <e> <f> <g> <h> [a b c d e f g h] 

"$*" is a single word, while "$@" expands to all the parameters as single words. Moreover, "$@" works correctly even if the arguments contain the first character of IFS.

0
set -- "hello there" bumblebee printf '%s\n' "$@" printf '%s\n' "$*" 

Result:

hello there bumblebee 

followed by

hello there bumblebee 

This shows that "$@" generates a list of individually quoted elements while "$*" generates a single quoted string.

Using bash, this can also be illustrated by a short shell script that takes a number of command line arguments:

#!/bin/bash IFS='_' atarray=( "$@" ) stararray=( "$*" ) printf 'at: %s\n' "${atarray[@]}" printf 'star: %s\n' "${stararray[@]}" 

Running it:

$ bash ./script.sh "one two" three four at: one two at: three at: four star: one two_three_four 

This also shows that using "$*" will use the value in $IFS (the first character only) to delimit the elements in the string resulting from the expansion.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.