Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does echo "$@" not show the script name but explicit iteration does?

Tags:

bash

Please consider the script foo.bash

#!/bin/env bash

echo "$@"
echo

for x in "${@:0:$# + 1}" ; do
    echo "$x"
done

Then ./foo.bash a b c gives the output

a b c

./foo.bash
a
b
c

As far as I understand the documentation, @ holds the positional arguments, so the output of echo "$@" makes sense to me.

But why does the for loop print the script name?

edit:

I'm using Git BASH on Windows, echo $BASH_VERSION yields 4.4.23(1)-release

like image 304
actual_panda Avatar asked Mar 03 '21 08:03

actual_panda


2 Answers

This behavior is specific to bash ver 4+.

As per man bash

@ Expands to the positional parameters, starting from one. That is, "$@" is equivalent to "$1" "$2" "$3" ...

However:

${parameter:offset:length} Substring Expansion. Expands to up to length characters of the value of parameter starting at the character specified by offset. If parameter is @, an indexed array subscripted by @ or *, or an associative array name, the results differ as described below. If length is omitted, expands to the substring of the value of parameter starting at the character specified by offset and extending to the end of the value. length and offset are arithmetic expressions (see ARITHMETIC EVALUATION below). If parameter is @, the result is length positional parameters beginning at offset. If parameter is an indexed array name subscripted by @ or *, the result is the length members of the array beginning with ${parameter[offset]}.

Substring indexing is zero-based unless the positional parameters are used, in which case the indexing starts at 1 by default. If offset is 0, and the positional parameters are used, $0 is prefixed to the list.

So if you use:

"${@:0}"

That will also include $0

however as mentioned earlier:

"$@"

will start from position one.

PS: Note that in previous bash versions, behavior is different and "${@:0}" won't include $0.

like image 113
anubhava Avatar answered Sep 28 '22 05:09

anubhava


This is exactly what Ksh and Zsh also do (and the substring/array slice notation is likely to originate from Ksh).

The point of $@ only giving the positional parameters and not $0, is that it can be used in loops like for x in "$@"; do ... (though you could just use for x do ...) or passed to other commands, as in somecmd "$@", possibly after some shifting to modify the list. Mixing $0 in the same list is very rarely useful.

But in ${@:0:n}, you explicitly ask for index 0, too. There's really no reason not to give the user the possibility of including $0, since if they don't want it, they could use $@ or ${@:1} instead.

With regular arrays, ${array[0]} is a regular element, so is naturally included in both ${array[@]} and ${array[@]:0}.

like image 26
ilkkachu Avatar answered Sep 28 '22 05:09

ilkkachu