Bash @ parameter - "special" array? What is it exactly?

46 views Asked by At

I'm trying to understand how (ba)sh works "under the hood". I noticed one problems that bothers me. Most of parameters such as @, ? or even * can be treated as readonly variables. They can be assigned to variables that will have exactly the same "value" (e.g. STAR_VAR=$*).

@ is different, especially when used as "$@". It's more like array (let's call it SET), which can be accessed with special non-array syntax, e.g. "$@" instead of "${SET[@]}". You can learn how and when to use it, but still - is's quite confusing way of accessing "numbered" parameters (it's more like array but you're accessing with non-array syntax).

Bash was improved a lot to replace legacy constructs like `` with more "clean" syntax like $(). I wonder why this aspect was not cleaned up to make bash syntax less tricky, eg. adding to shell's or function's environment array variable (e.g. SET) with "numbered" parameters inside (with option to use legacy syntax of course)?

2

There are 2 answers

5
KamilCuk On

What is it exactly?

"$@" is a super very special POSIX (not Bash) hack that is handled super very special specially with super very special syntax as specified by the POSIX shell specification https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_05_02 .

Bash was improved a lot to replace legacy constructs like `` with more "clean" syntax like $()

The $(...) notation was introduced by Ksh, not by Bash.

why this aspect was not cleaned up to make bash syntax less tricky

Portability. Bash keeps portability with POSIX. "$@" is very POSIX. According to wiki, introduced in SVR3 shell (1986).

how (ba)sh works "under the hood"

Bash works "under the hood" exactly the same way it is specified to. The "$@" is replaced by words of positional arguments, without running word splitting and filename expansion over those words and without joining those words. The source code is here https://github.com/bminor/bash/blob/f3b6bd19457e260b65d11f2712ec3da56cef463f/subst.c#L10993 and https://github.com/bminor/bash/blob/f3b6bd19457e260b65d11f2712ec3da56cef463f/subst.c#L10453 .

1
John Bollinger On

Bash @ parameter - "special" array? What is it exactly?

It is a standard POSIX feature aimed directly at the problem of representing positional arguments as a group while preserving the identity and contents of each individual parameter. It is provided alongside the * parameter exactly for the difference between the two when they are referenced inside double quotes (which is almost always how you want to expand them, especially $@).

The @ parameter is not an array because the POSIX shell language does not have arrays, though array support is provided as an extension by many popular implementations, such as Bash and Zsh. @ is simply a one-off, special case shell parameter with unique (in POSIX) expansion behavior.

You can learn how and when to use it,

You need to learn how and when to use it, because you can't get its particular behavior any other way, and if you're writing scripts very often then you'll definitely need that behavior from time to time.

but still - is's quite confusing way of accessing "numbered" parameters (it's more like array but you're accessing with non-array syntax).

I don't really see what's confusing about it. You don't use $@ to access individual positional parameters, only to access all of them collectively. The only other way to do that is $*. Both should pretty much always be expanded within double quotes to avoid unwanted word splitting or pathname expansion, so there's generally only one choice to make: do you want the results all as one unit ("$*") or as one unit per parameter ("$@")?

Bash was improved a lot to replace legacy constructs like `` with more "clean" syntax like $().

POSIX specifies $(), and several other enhancements over the original Bourne shell. I'm not sure what other constructs you would consider "legacy" in this sense.

I wonder why this aspect was not cleaned up to make bash syntax less tricky, eg. adding to shell's or function's environment array variable (e.g. SET) with "numbered" parameters inside (with option to use legacy syntax of course)?

Because $@ is standardized, well understood (current question notwithstanding), and works fine. Bash needs to support it for POSIX compatibility. Even if POSIX added arrays to the standard shell language, it would need to retain $@ for compatibility with half a century's worth of shell scripts. And if you already have $@ then what advantage is to be gained by adding an alias for it? Who wants to write "${SET[@]}" when they could instead just write "$@"? Who wants to write "${SET[1]}" when they could instead write "$1"? Or would the equivalent actually be "$2", since in Bash and Zsh, array indexing is 0-based?

But if you think you actually do want that, then in Bash or Zsh you can make it yourself:

SET=("$@")

or perhaps

SET=("$0" "$@")

Overall, you seem to be looking to classify $@ as an instance of something more fundamental, but in fact, there is nothing else underneath. And nobody particularly wants that. The shell language would need to be a lot different before there was much use for the kind of alternative you propose.