help-bash
[Top][All Lists]
Advanced

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

Re: Use of $@


From: Andreas Kusalananda Kähäri
Subject: Re: Use of $@
Date: Tue, 21 Feb 2023 12:13:42 +0100

On Tue, Feb 21, 2023 at 11:03:57AM +0000, Kerin Millar wrote:
> On Tue, 21 Feb 2023 11:27:36 +0100
> Christof Warlich <cwarlich@gmx.de> wrote:
> 
> > Hi,
> > 
> > just to improve my bash skills: The following functions prints the array
> > index of a value if found:
> > 
> > index() { local e="$1"; shift; local a=("$@"); for i in "${!a[@]}"; do
> > [[ ${a[$i]} != $e ]] || { echo $i; break; }; done; }
> > 
> > Thus, with e.g.: myarray=("a" "bc" "my value" "z")
> > 
> > I get:
> > 
> > $ index "my value" "${myarray[@]}"
> > 2
> > 
> > as expected. The only thing that bothers me is that I couldn't get away
> > without the intermediate assignment of $@ to a new array (a): Is there
> > really no way to avoid that, i.e. directly using $@ in the for-loop?
> 
> Unfortunately, the function is incorrect to begin with.

This depends on the semantics that you give to the index function. If
you want to get the index in the *array* of the element, yes, then you
have to deal with name references or indirection to examine the actual
array.  If you want to get the index of the element in the list of
arguments to the function, you obviously can use the original approach.
The original approach would also be valid in the cases where you don't
deal with sparse arrays (in fact, I have not seen any "live" code that
uses the fact that bash has sparse arrays).

.

> 
> $ myarray=(foo bar baz)
> $ index bar "${myarray[@]}"
> 1
> $ unset -v 'myarray[0]'
> $ declare -p myarray
> declare -a myarray=([1]="bar" [2]="baz")
> $ index bar "${myarray[@]}"
> 0
> 
> Indeed, to correctly encapsulate the intended functionality with a function 
> is a tremendously awkward affair in bash. One option would be to pass the 
> array by its name rather than as its expanded elements, then make use of a 
> nameref in the function. Unfortunately, namerefs are poorly implemented in 
> bash [1] [2]. Should you wish to try it anyway, here is an example.
> 
> index() {
>       local i e="$1"
>       local -n _ref="$2"
>       shift
>       for i in "${!_ref[@]}"; do
>               if [[ ${_ref[i]} == "$e" ]]; then
>                       printf '%s\n' "$i"
>                       return 0
>               fi
>       done
>       return 1
> }
> 
> $ declare -p myarray
> declare -a myarray=([1]="bar" [2]="baz")
> $ index bar myarray
> 1
> 
> One of the many issues with namerefs is that they can easily be undone by a 
> (variable) name space conflict. I do not know which underlying problem the 
> function is intended to solve but it is quite possible that, whatever it may 
> be, you might benefit from using an associative array [1]. Not least, an 
> associative array could easily be made to store a 'reverse' map of elements 
> to index numbers.
> 
> On the other hand, if you are entirely certain that the function will only 
> ever have to contend with non-sparse arrays in your program, you could just 
> loop over the positional parameters ("$@") and use an incrementing counter 
> variable to identify the appropriate index number.
> 
> P.S. The right-hand side of == in [[ is treated as a globbing pattern if not 
> appropriately quoted.
> 
> [1] http://mywiki.wooledge.org/BashFAQ/006 
> [2] https://gist.github.com/ormaaj/5682807
> 
> -- 
> Kerin Millar

-- 
Andreas (Kusalananda) Kähäri
SciLifeLab, NBIS, ICM
Uppsala University, Sweden

.



reply via email to

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