367

In many SO questions and bash tutorials I see that I can access command line args in bash scripts in two ways:

$ ~ >cat testargs.sh 
#!/bin/bash

echo "you passed me" $*
echo "you passed me" $@

Which results in:

$ ~> bash testargs.sh arg1 arg2
you passed me arg1 arg2
you passed me arg1 arg2

What is the difference between $* and $@?
When should one use the former and when shall one use the latter?

Seanny123
  • 6,594
  • 11
  • 56
  • 106
oz123
  • 23,317
  • 25
  • 106
  • 169

5 Answers5

480

The difference appears when the special parameters are quoted. Let me illustrate the differences:

$ set -- "arg  1" "arg  2" "arg  3"

$ for word in $*; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in $@; do echo "$word"; done
arg
1
arg
2
arg
3

$ for word in "$*"; do echo "$word"; done
arg  1 arg  2 arg  3

$ for word in "$@"; do echo "$word"; done
arg  1
arg  2
arg  3

one further example on the importance of quoting: note there are 2 spaces between "arg" and the number, but if I fail to quote $word:

$ for word in "$@"; do echo $word; done
arg 1
arg 2
arg 3

and in bash, "$@" is the "default" list to iterate over:

$ for word; do echo "$word"; done
arg  1
arg  2
arg  3
glenn jackman
  • 207,528
  • 33
  • 187
  • 305
  • 75
    +1 I've always thought this concept was best demonstrated by a simple example, in which the bash manual is completely lacking. – chepner Sep 07 '12 at 12:01
  • 5
    Is there a possible use case, when `$*` or `"$*"`may be required, & the purpose cannot be served by `$@` or `"$@"`? – anishsane Aug 03 '13 at 11:17
  • Also, is there a difference between `${arrayname[*]}` vs `${arrayname[@]}`? – anishsane Aug 03 '13 at 11:18
  • 5
    Which version is more suitable for a "wrapper" script, where the scripts parameters need to become parameters to a new command? – Segfault Mar 30 '15 at 15:53
  • 7
    @Segfault, in this case, always choose `"$@"` with the quotes. – glenn jackman Mar 30 '15 at 16:09
  • 4
    This answer contains useful examples but in would be better if it also explained the mechanism behind them. *Why* does it work like this? – Lii Jul 28 '16 at 17:45
  • 1
    Instead of `for word ...` loop, you could write: `printf "%s\n" "$*"` to be compared to `printf "%s\n" "$@"` and `printf "%s\n" $@`! (... or even `printf %s\\n "$@"` ;) – F. Hauri Jan 30 '20 at 17:32
  • I wonder why bash is designed to behave like this... It seems quite strange IMHO... – ch271828n Jan 20 '21 at 13:09
  • 1
    bash is a strange language. In many ways I think it is constrained by compatibility with the 40+ year old Bourne shell – glenn jackman Jan 20 '21 at 15:15
283

A nice handy overview table from the Bash Hackers Wiki:

Syntax Effective result
$* $1 $2 $3 … ${N}
$@ $1 $2 $3 … ${N}
"$*" "$1c$2c$3c…c${N}"
"$@" "$1" "$2" "$3" … "${N}"

where c in the third row is the first character of $IFS, the Input Field Separator, a shell variable.

If the arguments are to be stored in a script variable and the arguments are expected to contain spaces, I wholeheartedly recommend employing a "$*" trick with the input field separator set to tab IFS=$'\t'.

Serge Stroobandt
  • 19,748
  • 8
  • 84
  • 81
  • Here is [an example](https://stackoverflow.com/a/4824637/2192488), which includes quoted input. The input also matters! – Serge Stroobandt Oct 17 '17 at 20:57
  • Let's say I want to create a wrapper script that does nothing but mimic the functionality of the wrapped command. Which syntax should I use to pass the args from the wrapper script to inner command? – Marinos An Apr 08 '20 at 06:21
46

$*

Expands to the positional parameters, starting from one. 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. If IFS is unset, the parameters are separated by spaces. If IFS is null, the parameters are joined without intervening separators.

$@

Expands to the positional parameters, starting from one. When the expansion occurs within double quotes, each parameter expands to a separate word. That is, "$@" is equivalent to "$1" "$2" ... If the double-quoted expansion occurs within a word, the expansion of the first parameter is joined with the beginning part of the original word, and the expansion of the last parameter is joined with the last part of the original word. When there are no positional parameters, "$@" and $@ expand to nothing (i.e., they are removed).

Source: Bash man

Muffo
  • 1,573
  • 1
  • 15
  • 27
18

$@ is same as $*, but each parameter is a quoted string, that is, the parameters are passed on intact, without interpretation or expansion. This means, among other things, that each parameter in the argument list is seen as a separate word.

Of course, "$@" should be quoted.

http://tldp.org/LDP/abs/html/internalvariables.html#ARGLIST

rkosegi
  • 12,129
  • 5
  • 46
  • 75
1

This example let may highlight the differ between "at" and "asterix" while we using them. I declared two arrays "fruits" and "vegetables"

fruits=(apple pear plumm peach melon)            
vegetables=(carrot tomato cucumber potatoe onion)

printf "Fruits:\t%s\n" "${fruits[*]}"            
printf "Fruits:\t%s\n" "${fruits[@]}"            
echo + --------------------------------------------- +      
printf "Vegetables:\t%s\n" "${vegetables[*]}"    
printf "Vegetables:\t%s\n" "${vegetables[@]}"    

See the following result the code above:

Fruits: apple pear plumm peach melon
Fruits: apple
Fruits: pear
Fruits: plumm
Fruits: peach
Fruits: melon
+ --------------------------------------------- +
Vegetables: carrot tomato cucumber potatoe onion
Vegetables: carrot
Vegetables: tomato
Vegetables: cucumber
Vegetables: potatoe
Vegetables: onion
stefansson
  • 161
  • 1
  • 5