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?
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
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, load them in an array variable.
$*
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
$# 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
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
Related
This question already has answers here:
Accessing bash command line args $# vs $*
(5 answers)
What is the difference between "$#" and "$*" in Bash? [duplicate]
(2 answers)
Closed 9 years ago.
I have wrote a simple script that takes in any number of parameters to demonstrate the difference between $# and $*:
#!/bin/bash
echo "double quoted $* $#"
echo 'single quoted $* $#'
On the CLI I did
$./stuff.sh a b c d e f dfs
And this is what prints out
double quoted a b c d e f dfs a b c d e f dfs
single quoted $* $#
Since they are identical does that mean $# equals to $*? Or is there a point I am missing?
From Special Parameters in Bash Reference Manual
*
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).
Better with an example:
$ d=(a b c)
$ for i in "${d[*]}"; do echo $i; done <---- it is a field all together
a b c
$ for i in "${d[#]}"; do echo $i; done <---- each item is a different field
a
b
c
$ set -- "hello world" foo bar
$ printf '<%s> ' "$#"; echo
<hello world> <foo> <bar> # "$#" keeps original separation
$ printf '<%s> ' "$*"; echo
<hello world foo bar> # "$*" combines everything into one string
$ printf '<%s> ' $*; echo
<hello> <world> <foo> <bar> # $* (no quotes) loses delimitation entirely.
$ printf '<%s> ' $#; echo
<hello> <world> <foo> <bar> # unquoted, $# is identical to unquoted $*.
Technically, $* isn't limited to space-delimiting; it uses the first character of $IFS. This can be used to your advantage:
jars=( *.jar )
IFS=:
export CLASSPATH=${jars[*]}
From http://linuxsig.org/files/bash_scripting.html:
$* Stores all the arguments that were entered on the command line
so
$1 $2 ...
"$#" Stores all the arguments that were entered on the command line, individually quoted
so
"$1" "$2" ...
I am having some troubles and misunderstanding about how are variable assignment and parenthesis handled in the different existing shells.
What is currently puzzling me is the following:
Always using the following command
./script.sh a b c d
when running the following code
#!/bin/zsh
bar=$#
for foo in $bar
do
echo $foo
done
the output is
a b c d
and with
#!/bin/zsh
bar=($#)
for foo in $bar
do
echo $foo
done
it is (what I initially wanted)
a
b
c
d
but using bash or sh
#!/bin/bash
bar=$#
for foo in $bar
do
echo $foo
done
gives
a
b
c
d
and
#!/bin/bash
bar=($#)
for foo in $bar
do
echo $foo
done
it is just
a
What is going on there ?
Joint operations
For both of the shells involved, the examples given will assume an explicitly set argv list:
# this sets $1 to "first entry", $2 to "second entry", etc
$ set -- "first entry" "second entry" "third entry"
In both shells, declare -p can be used to emit the value of a variable name in unambiguous form, though how they represent that form can vary.
In bash
Expansion rules in bash are generally compatible with ksh and, where applicable, POSIX sh semantics. Being compatible with these shells requires that unquoted expansion perform string-splitting and glob expansion (replacing * with a list of files in the current directory, for instance).
Using parenthesis in a variable assignment makes it an array. Compare these three assignments:
# this sets arr_str="first entry second entry third entry"
$ arr_str=$#
$ declare -p arr_str
declare -- arr="first entry second entry third entry"
# this sets arr=( first entry second entry third entry )
$ arr=( $# )
declare -a arr='([0]="first" [1]="entry" [2]="second" [3]="entry" [4]="third" [5]="entry")'
# this sets arr=( "first entry" "second entry" "third entry" )
$ arr=( "$#" )
$ declare -p arr
declare -a arr='([0]="first entry" [1]="second entry" [2]="third entry")'
Similarly, on expansion, quotes and sigils matter:
# quoted expansion, first item only
$ printf '%s\n' "$arr"
first entry
# unquoted expansion, first item only: that item is string-split into two separate args
$ printf '%s\n' $arr
first
entry
# unquoted expansion, all items: each word expanded into its own argument
$ printf '%s\n' ${arr[#]}
first
entry
second
entry
third
entry
# quoted expansion, all items: original arguments all preserved
$ printf '%s\n' "${arr[#]}"
first entry
second entry
third entry
In zsh
zsh does a great deal of magic to try to do what the user means, rather than what's compatible with historical shells (ksh, POSIX sh, etc). However, even there, doing the wrong thing can have results other than what you want:
# Assigning an array to a string still flattens it in zsh
$ arr_str=$#
$ declare -p arr_str
typeset arr_str='first entry second entry third entry'
# ...but quotes aren't needed to keep arguments together on array assignments.
$ arr=( $# )
$ declare -p arr
typeset -a arr
arr=('first entry' 'second entry' 'third entry')
# in zsh, expanding an array always expands to all entries
$ printf '%s\n' $arr
first entry
second entry
third entry
# ...and unquoted string expansion doesn't do string-splitting by default:
$ printf '%s\n' $arr_str
first entry second entry third entry
When you do this:
bar=($#)
You're actually creating a bash shell array. To iterate a bash array use:
bar=( "$#" ) # safer way to create array
for foo in "${bar[#]}"
do
echo "$foo"
done
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?
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
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, load them in an array variable.
$*
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
$# 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
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
This question already has answers here:
Accessing bash command line args $# vs $*
(5 answers)
What is the difference between "$#" and "$*" in Bash? [duplicate]
(2 answers)
Closed 9 years ago.
I have wrote a simple script that takes in any number of parameters to demonstrate the difference between $# and $*:
#!/bin/bash
echo "double quoted $* $#"
echo 'single quoted $* $#'
On the CLI I did
$./stuff.sh a b c d e f dfs
And this is what prints out
double quoted a b c d e f dfs a b c d e f dfs
single quoted $* $#
Since they are identical does that mean $# equals to $*? Or is there a point I am missing?
From Special Parameters in Bash Reference Manual
*
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).
Better with an example:
$ d=(a b c)
$ for i in "${d[*]}"; do echo $i; done <---- it is a field all together
a b c
$ for i in "${d[#]}"; do echo $i; done <---- each item is a different field
a
b
c
$ set -- "hello world" foo bar
$ printf '<%s> ' "$#"; echo
<hello world> <foo> <bar> # "$#" keeps original separation
$ printf '<%s> ' "$*"; echo
<hello world foo bar> # "$*" combines everything into one string
$ printf '<%s> ' $*; echo
<hello> <world> <foo> <bar> # $* (no quotes) loses delimitation entirely.
$ printf '<%s> ' $#; echo
<hello> <world> <foo> <bar> # unquoted, $# is identical to unquoted $*.
Technically, $* isn't limited to space-delimiting; it uses the first character of $IFS. This can be used to your advantage:
jars=( *.jar )
IFS=:
export CLASSPATH=${jars[*]}
From http://linuxsig.org/files/bash_scripting.html:
$* Stores all the arguments that were entered on the command line
so
$1 $2 ...
"$#" Stores all the arguments that were entered on the command line, individually quoted
so
"$1" "$2" ...
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?
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
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, load them in an array variable.
$*
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
$# 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
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