By Mohamad Wael

Posted :

What is the difference between $@ and $* ?

What is CGI , fastCGI , fcgiwrap , FastCGI Development Kit , featured image

I think it is easier to understand this issue , in the following manner . Say you have this bash script :

#!/bin/bash

ARG='1 2 3'

for val in "$ARG"
do
    echo $val;
done

#test.sh
# First example

If you go and run it , using ./test.sh , the output will be 1 2 3 .

Now , if you write the same script , without quoting the ARG variable , as in for val in $ARG , simply put you are not quoting the ARG variable value , hence it is as if , the for loop is receiving 1 2 3 , and not "1 2 3" , as such the result is :

1
2
3

The provided examples can be used to understand how $@ , "$@" , $* , "$*" , work in bash .

$@ and $* , both expand to the positional arguments passed to the script , for example if a script named test is run using ./test 1 2 "3 4" , then it has three positional parameters , which are 1 , 2 , and "3 4" . Hence $@ and $*, will expand to a line formed of the three words 1 2 3 4 , which are not quoted .

This means if $@ or $* are used in a for loop , or with a command such as rm , the command or the for loop will receive a line formed of words which are not quoted , as in 1 2 3 4 , instead of 1 2 "3 4" , so further tokenization might happen , as in what was originally "3 4" , is interpreted as 3 4 , hence as the separate words 3 and 4 .

To illustrate this with an example , imagine having a script of :

#!/bin/bash

for val in $@
do
    echo $val
done

#test.sh

And you run it , using the command ./test.sh 1 2 'a b' , then the output will be :

1
2
a
b

The result is the same if for val in $* is used .

When $@ is quoted as in "$@" , each individual positional parameter is quoted , so you can think of it , as quote at each position , so for example if the passed arguments where 1 2 "3 4" , the value of "$@" which is viewed by a command or a for loop is "1" "2" "3 4" .

To illustrate this with an example , imagine having the script :

#!/bin/bash

for val in "$@"
do
    echo $val
done

#test.sh

And you run it with the command ./test.sh 1 2 'a b' , then the output will be :

1
2
a b

When "$*" is quoted , then all the arguments are quoted into a single word , and they are connected using the first character of the IFS variable .

If IFS is unset , as in unset IFS , then a whitespace is the joining character , as in "$1 $2 .." , whereas if IFS has a null value , as in IFS= , then the arguments are joined without using any separator , as in "$1$2..." .

Being quoted , this means that no more word breakage occur , when using "$*" , in a command or in a for loop .

This can be illustrated with an example :

#!/bin/bash

for val in "$*"
do
    echo $val
done

#test.sh

When running this script using , ./test.sh 1 2 'a b' , this will output 1 2 a b , because for the for loop the value which is being passed is "1 2 a b" , and not 1 2 a b .

Concerning zsh , it acts a little bit differently then bash . First of all , when dereferencing a variable using $ , and if the variable has a textual value , then this variable is not split furthermore on space , unless the SH_WORD_SPLIT option is set . So it is as if the variable value is quoted .

#!/bin/zsh

ARG='1 2 3'

for val in $ARG
do
    echo $val
done

# Will output
# 1 2 3


set -o SH_WORD_SPLIT

for val in $ARG
do
    echo $val
done
# Will output
# 1
# 2
# 3

If the variable is an array , and the option KSH_ARRAYS is not set , then each element in the array , is expanded into a word , otherwise if the KSH_ARRAYS option is set , then only the first element of the array is expanded to a word . Word splitting is always controlled using the variable SH_WORD_SPLIT .

#!/bin/zsh

ARG=(1 2 3)
for val in $ARG
do
    echo $val
done

# Will output
# 1
# 2
# 3

set -o KSH_ARRAYS

for val in $ARG
do
    echo $val
done
# Will output
# 1

In zsh , the special parameters * , and @ , are actually arrays , but what is important to understand , is that $* , and $@ , when being unquoted , they act the same , they simply expand to a line formed of words , where each word is an element in the array . The SH_WORD_SPLIT option controls the splitting of a word .

#!/bin/zsh

for val in $*
do
    echo $val
done
# If run using
#   ./test.sh '3 4' 1 2
#   , then the output
#   will be :
# 3 4
# 1
# 2
# A string formed of three words ,
#   is formed from the array ,
#   since the SH_WORD_SPLIT
#   is not set , no further
#   splitting is performed .


set -o KSH_ARRAYS
set -o SH_WORD_SPLIT
# set the KSH_ARRAYS ,
#   and the SH_WORD_SPLIT
#   options .

for val in $@
do
    echo $val
done
# If run using
#   ./test.sh '3 4' 1 2
#   , then the output
#   Will be :
# 3
# 4
# 1
# 2
# The KSH_ARRAYS options
#   does not affect $* and $@ .
# A string formed of three words ,
#   is created , the SH_WORD_SPLIT
#   option is set , this means that
#   a word is split on whitespace .

When $* is quoted as in "$*" , it evaluates to a quoted word , formed of the concatenation of all the elements in the array , joined by the first character of the IFS variable if set . If the IFS variable is not set , as in unset IFS , then a space character is used for joining the elements of the array . If the IFS variable is set to a null value , as in IFS= , then the elements of the array , are concatenated without using any separator .

When $@ is quoted as in "$@" , then each word of the formed string , acts as being quoted , hence each word of the formed string , is not affected by the setting of SH_WORD_SPLIT , even when being set to on .

#!/bin/zsh

set -o KSH_ARRAYS
set -o SH_WORD_SPLIT
# Set the KSH_ARRAYS ,
#   and the  SH_WORD_SPLIT
#   options to on .


for val in "$*"
do
    echo $val
done

# When run using
#   ./test.sh '3 4' 1 2
#   , the output
#   will be :
# 3 4 1 2
# $* is quoted , hence
#   all the elements of
#   the array are joined
#   into a single quoted
#   word '3 4 1 2' .


for val in "$@"
do
    echo $val
done
# When run using
#   ./test.sh '3 4' 1 2
#   , the output Will
#   be :
# 3 4
# 1
# 2
# Since $@ is quoted ,
#   a string is formed 
#   from the element of 
#   the array . 
# Each word of this string 
#   corresponds to an element 
#   of the array .
# Each word of this 
#   string is quoted , 
#   hence it is not
#   affected by the value
#   of SH_WORD_SPLIT