ivan
ivan

Reputation: 6322

Shell parameter expansion: $# vs. ${#@}

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?

Upvotes: 1

Views: 368

Answers (1)

mklement0
mklement0

Reputation: 437111

${#@} / ${#*} 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.


Optional background information

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}.

Upvotes: 5

Related Questions