bug-bash
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Re: Question on $@ vs $@$@


From: Greg Wooledge
Subject: Re: Question on $@ vs $@$@
Date: Thu, 22 Aug 2024 20:05:51 -0400

On Fri, Aug 23, 2024 at 01:28:49 +0200, Steffen Nurpmeso wrote:
>   a() { echo $#,1=$1,2=$2,"$*",$*,; }
>   set -- a b c
>   echo 2
>   IFS=:; echo "$*"$*; a $* "$*";

Your printing function "a" is highly questionable.  It's got unquoted
word expansions that'll do who knows what, and it's also using echo
which may interpret contents and alter them.

If you want to see the arguments given, I recommend this instead (assuming
bash):

args() {
    if (($#)); then
        printf '%d args:' "$#"
        printf ' <%s>' "$@"
        echo
    else
        echo "0 args"
    fi
}

If you need it to work in sh, replace (($#)) with something like
test "$#" != 0.  You could also write a script named args which performs
the same steps, so that it can be invoked from anywhere.  Here's mine:


hobbit:~$ cat bin/args
#!/bin/sh
printf "%d args:" "$#"
test "$#" -gt 0 && printf " <%s>" "$@"
echo


> Why does bash (aka sh(1)) word splits
> 
>   IFS=:; echo "$*"$*; a $* "$*";
>                         ^
>                         this
> 
> unquoted $* at SPACE into three different tokens?

Unquoted $* is expanded in three passes.  The first pass generates one word
per parameter.  The second pass applies word-splitting (based on IFS) to
each word.  The third pass applies globbing (pathname expansion) to each
of *those* words.

So, you're expanding $* to <a> <b> <c>.  Since none of those words
contains any IFS characters, or any globbing characters, that's the
final list.

You're therefore calling a with 4 parameters:

    a "a" "b" "c" "a:b:c"

> then bash gives me
> 
>   2
>   a:b:ca b c
>   4,1=a,2=b,a:b:c:a:b:c,a b c a b c,

Your printing function contains an unquoted $*, so we repeat the same
procedure.  You have 4 parameters, so $* is first expanded to a list
of one word per parameter: <a> <b> <c> <a:b:c>.

Next, since IFS is set to : at this point, the fourth word is split into
a new list of words, and now you have <a> <b> <c> <a> <b> <c>.

Finally, globbing would apply.  In this case, there are no globbing
characters, so no further changes occur.

Ultimately, you call echo with the following arguments:

    echo "4,1=a,2=b,a:b:c:a:b:c,a" "b" "c" "a" "b" "c,"

echo doesn't care about IFS, so it writes these arguments with spaces
between them, and therefore you see the output that you see.

Unquoted $* and $@ are almost always errors.  You should not use them
without an extremely good reason.

In 90% of cases, what you want is "$@" which preserves the parameter
list.  In 9% of cases, you want "$*" which concatenates all the
parameters together into a single string, for reporting.



reply via email to

[Prev in Thread] Current Thread [Next in Thread]