Tricky array substitution in functions

Hello,

Please tell me if there is a better way to get the number of elements from an array that is passed to a function.

This is what works on Solaris 8 (ksh) but it looks odd:

loop_array() {

array_name=$2

        b1='\${\#'
        b2='[@]}'

        nr_elements=`eval echo $b1$array_name$b2`
        # this one prints the actual variable I want to reassign ...
        echo $nr_elements
        # it seems to only work like this
          nr_elements=`eval echo $nr_elements`
          echo $nr_elements
        # this line prints out the number of elements correctly but when I assign it to a variable with ` ` I get bad substitution error
        eval echo \${\#$array_name[@]}

#...
}

Thanks

I guess the 'last' acctualy is the 'nr_elements' or you have something missed.

If it is, let see what the 'last' has:

You have build correct string "\${\#$array_name[@]}"; asked to 'eval'uate it - have in result "${#<arr_name>[@]}"; and asking to execute it!
Try execute this line with correct array name: you will have :command not found!
Because it will produce a number of the array elements and a number is not a command by itself.

I did not clear understand what do you need: the correct number of element for any given array or the command itself into a variable?

If the array element number, you do not need to escape the characters in b1 and b2 (acctualy, I do not see why do you need them) :

b1='${#';
b2='[@]}'
eval echo $b1$array_name$b2

I do not see any reason to build a command itself and after that execute it. It will required double execution, as you already figured it out.

So, you have an array name in a variable and would like to call a function that returns the aray elements number? A function need to be executed to get a result. So:

>elms(){ eval echo \${\#$1[@]\}; return 0;}

># - checking:
>a1=(aa dd cc rr);
>a2=(klkl ioio opo);
> echo `elms a1`
4
> echo $(elms a2)
3

To get just a "echo $nr_elements" the nr_elements should be a variable, which you could set in a function. So

>set_elms_n(){ elm_n=$(eval echo \${\#$1[@]\}); return 0;}
>set_elms_n a1
>echo $elm_n;
4
>set_elms_n a2
>echo $elm_n;
3

That what you was looking for?

This is partially what I wanted, thank you for the tips.

What I need is to use the variable inside the function itself not outside.

For example:

set_elms_n(){ elm_n=$(eval echo \${\#$1[@]\}); echo $elem_n; return 0;}

# when I run "set_elms_n a1" I get no result.

The rest of the function continues with a loop through the array backwards(decrementing the number of lines variable), so that is why I need the variable to be set inside the function.

:slight_smile:

  • you just misspell variable in function!
> set_elms_n(){ elm_n=$(eval echo \${\#$1[@]\}); echo $elm_n; return 0;}
> set_elms_n a1
4

try this as well:

# pass arrays by value not by "reference"
trn_arr()
{
   
   set -A arr $1
   echo "array length= ${#arr[*]}"
   pos=$2
   pos=$(( pos -1 ))
   echo " $2 element of array = ${arr[pos]}"
}

set -A myarr 9 8 7 6 5 4 3 2 1
trn_arr "${myarr[*]}"  4

It is oversimlified.
Also the array is redefined (acctually, it is where the problem set):

> a11=("1-two words" 2-jkjk 3-uiuiu 4-opopop 5-tytyty)
> ec ${#a11[@]}
5
> trn_arr "${a11[*]}"  2
array length= 6
 2 element of array = words
>

Just recall this last pointed by Jim try to send an array by value, instead of by reference (in shell - just the name.)
The point is that it could be done in correct way, but with appropriate call syntaxis:

>
> arr=("two words" "one" "more")
>
> prt_arr(){ 
> n_el=$#;        # number of positional parameters could be the same as in arr
>                   # the positional parameters are array, acctualy; 
>                   # no needs to re-assign
>                   # could be accesed with simplified way: $1, $2, .. that is 
>                   # the same, as ${arr[0]}, ${arr[1]}, ...
> while [[ ! -z $* ]]; 
>   do /usr/bin/echo "$1\n"; 
>   shift; 
> done; 
> echo "orig number : $n_el";}
>
>    # now it is important to call in correct way: with @ and in ""
> prt_arr "${arr[@]}"
two words

one

more

orig number : 3
>
>    # other way call does not produce expected result:
> prt_arr ${arr[@]}
two

words

one

more

orig number : 4
> prt_arr ${arr[*]}
two

words

one

more

orig number : 4
> prt_arr "${arr[*]}"
two words one more

orig number : 1
>