Within a shell script, as far as I can tell, $#
and ${#@}
behave identically, both giving the number of positional parameters. Is there any difference between the two, and in what context would one be preferable over the other?
${#@}
/ ${#*}
is the same as $#
in most POSIX-like shells, but not all - a notable exception is dash
, which acts as sh
on Ubuntu systems.
$#
is the POSIX-compliant form, so it is the safe (portable) choice (from the POSIX spec, prefix $
implied):
#
Expands to the decimal number of positional parameters.
The POSIX shell spec is largely based on the historical Bourne shell, whose only array-like construct is the sequence of positional parameters ($1
, $2
, ...), with $#
containing the count of positional parameters, $*
expanding to a space-separated list of the parameter values that is then subject to word-splitting, and "$@"
- in a double-quoted context - expanding to the positional parameters as originally specified (even if they contain embedded whitespace).
The following discusses bash
, ksh
, and zsh
; dash
, which acts fundamentally differently, is discussed at the bottom.
bash
, ksh
, and zsh
:
POSIX-compatible shells such as ksh
and bash
later generalized this pseudo-array to provide bona fide array variables, whose syntax borrowed from the positional-parameter syntax (zsh
supports this syntax too, but has its own, simpler syntax as well):
${arr[*]}
and "${arr[@]}"
function analogously to $*
and "$@"
, and both ${#arr[@]}
and ${#arr[*]}
correspond to $#
.
Perhaps in a nod to the original syntax, these shells (which also includes zsh
, whose array syntax is simpler, however) also chose to support ${#@}
and ${#*}
for symmetry, where you can think of @
/ *
as the all-elements subscripts of the implied array, i.e., the pseudo-array of positional parameters.
As for symmetry regarding element extraction:
Something like ${@[2]}
to mirror $2
works only in zsh
, not in bash
and ksh
.
The equivalent slicing syntax works in all of them, however: ${@:2:1}
dash
:
dash
, the default shell (/bin/sh
) on Ubuntu systems, dash
, is mostly restricted to POSIX-only features, and does not support arrays at all.
As a consequence, it treats ${#@}
/ ${#*}
differently: it interprets @
and *
as the scalar string list of the (expanded) parameters and returns that string's length.
In other words: in dash
, echo "${#@}
/ echo "${#*}
is the equivalent of: list="$@"; echo "${#list}"
.
In the absence of support for arrays altogether, dash
fittingly neither supports ${@[2]}
nor ${@:2:1}
.