0

I don't understand why this example script doesn't modify the contents of array2. It seems that we should be able to set array2 to be equal to array1 by iterating through array1's elements and setting the corresponding element by index of array2 to be equal to the element in array1

#!/bin/bash

array1=(1 2 3)
array2=()

modify_array2 () {
#set array2 to be equal to array1 by iterating through array1's 
#elements and setting the corresponding element by index of array2 by 
#to be equal to the element in array1

index=0
declare -a _array1=("${!1}")
declare -a _array2=("${!2}")
for i in "${_array1[@]}"; do
  _array2["$index"]="$i"
  ((index++))
done
}

modify_array2 array1[@] array2[@]
#this should permanently modify array2, however if we print the 
#contents of array2, we get nothing:

for i in "${array2[@]}"; do
  printf "$i\n"
done

output:

Shellcheck says the following:

Line 3: array1=(1 2 3) ^-- SC2034: array1 appears unused. Verify use (or export if used externally). Doesnt make sense, I'm clearly using it in:

modify_array2 array1[@] array2[@]

Line 11: _array2["$index"]="$i" ^-- SC2034: _array2 appears unused. Verify use (or export if used externally).

odd, because aren't I using it by expanding by using a positional parameter in:

modify_array2 array1[@] array2[@]

and also using in the function in:

_array2["$index"]="$i"

Line 19: printf "$i\n" ^-- SC2059: Don't use variables in the printf format string. Use printf "..%s.." "$foo".

Fine, but I tried echo as well, same result

2
  • 1
    Add a shebang and then paste your script there: shellcheck.net Commented Aug 26, 2018 at 15:17
  • added this, thanks Commented Aug 26, 2018 at 15:31

1 Answer 1

3

I see two issues with your script. The first one is that inside your modify_array2 function you use two declared local variables, that are created by the values passed as parameters. The second one is that you never modify the array2 variable.

A possible solution is:

#!/bin/bash

array1=(1 2 3)
array2=()

modify_array2 () {
    #set array2 to be equal to array1 by iterating through array1's 
    #elements and setting the corresponding element by index of array2 by 
    #to be equal to the element in array1

    index=0
    local -n _array1=$1
    local -n _array2=$2
    for i in "${_array1[@]}"; do
        _array2["$index"]="$i"
        ((index++))
    done
}

printf "These are the contents of array1 before calling the function:\n"
printf "%s\n" "${array1[@]}"

printf "These are the contents of array2 before calling the function:\n"
printf "%s\n" "${array2[@]}"

modify_array2 array1 array2

printf "These are the contents of array2 after calling the function:\n"
printf "%s\n" "${array2[@]}"

Instead of declare, I use local to create the two local variables. That is just my personal taste, as declare makes the variables local when it is used in a function as if they were created using local (See help declare).

But what is more relevant to the question is the way of handling the arguments passed to the function. In the original script they were passed by value. Thus it was impossible to make any change to the second array passed in the argument list.

In the proposed solution the function is called passing the arrays themselves (not their values). Inside the function two local variables are created as references to the arrays passed as arguments to the function. That's what the -n options means: to create a nameref (a reference) to another variable. If you look in the Bash man page for nameref you will see that "a nameref is commonly used within shell functions to refer to a variable whose name is passed as an argument to the function". That way any assignment you do to the reference is treated as assignment to the variable you passed as an argument.

Sign up to request clarification or add additional context in comments.

5 Comments

adding a quotation from manual about declaring a variable as nameref may make this answer better
Thanks, this seems to work. I tried to find local -n usage from documentation online because I had seen it before but surprisingly it doesn't seem well documented. I see how passing the values of the arrays could have been a problem but I don't understand yet how local vs global scope could have been an issue.
@Fingers: see help local.
@oguzismail I have added an extra paragraph with a mention to the nameref definition included in the Bash man page.
@Fingers I have edited the description of the proposed solution to clarify that you can use either local or declare, since declare creates the variables as if they were created by local when it is used in a function.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.