0

Suppose I have an array foo that looks like the below, notice the third element.

foo=("a" "b" "c c")
echo "${foo[2]}" # "c c"
echo "${#foo[@]}" # "3"

How might I create an exact duplicate of that array into a variable bar? And then if you want to pass that array (by value) into a function baz?

Edit: Moved answer to answer.

8
  • What are you trying to accomplish with the eval? (Why do you think you need eval to accomplish that thing?) Commented Nov 10, 2014 at 2:28
  • baz "${foo[@]}" is the accepted and conventional way to pass an array by value, which makes your values accessible as "$@" inside the function. What you're actually doing is not passing by value at all, but passing the name -- which is to say, passing by reference. Commented Nov 10, 2014 at 2:29
  • ...also, particularly for Bash 4.3 or newer, there are much better and safer ways to do pass-by-reference with no eval involved. Commented Nov 10, 2014 at 2:30
  • @CharlesDuffy If I understand baz "${foo[@]}" correctly, it means no other arguments can be passed to the function? Commented Nov 10, 2014 at 2:31
  • correct, unless you get creative. For instance, you can pass the array length before the array's values, and pop only that many items off the argv. Commented Nov 10, 2014 at 2:34

3 Answers 3

1

If you want to pass an array by reference (which is what your code actually does), bash 4.3 allows this to be done with namevars:

foo=( hello "cruel world" )

print_contents() {
  local -n array=$1
  printf '%q\n' "${array[@]}"
}

print_contents foo

If you want to pass by value, even easier (and functional even with ancient releases):

foo=( hello "cruel world" )

print_contents() {
  printf '%q\n' "$@"
}

print_contents "${foo[@]}"

If you want to pass multiple arrays by value, by contrast:

foo=( hello "cruel world" )
bar=( more "stuff here" )

print_arrays() {
  while (( $# )); do
    printf '%s\n' "Started a new array:"
    local -a contents=()
    local array_len
    array_len=$1; shift
    for ((n=0; n<array_len; n++)); do
      contents+=( "$1" ); shift
    done
    printf '  %q\n' "${contents[@]}"
  done
}

print_arrays "${#foo[@]}" "${foo[@]}" \
             "${#bar[@]}" "${bar[@]}"
Sign up to request clarification or add additional context in comments.

Comments

0

I ended up finding my answer as I was writing the question. Here's the train of thought.

Several attempts below

bar=foo           # String "foo"
bar=$foo          # First element of array foo "a"

bar=${foo[@]}
echo "${#bar[@]}" # "1"
echo "${bar[0]}"   # "a b c c"

bar=(${foo[@]})
echo "${#bar[@]}" # "4"
echo "${bar[2]}"   # "c"
echo "${bar[3]}"   # "c"

bar=("${foo[@]}")
echo "${#bar[@]}" # "3"
echo "${bar[2]}"   # "c c"




baz () {
  eval "bar=(\"\${$1[@]}\")"

  for item in "${bar[@]}"
  do
    echo "$item"
  done

  # a
  # b
  # c c
}

2 Comments

FYI -- your baz isn't declaring bar local, so it's actually modifying the global variable bar, rather than scoping that to your function.
...also, I'm unclear on how this is "by value" -- the $1 you pass in is the name of the variable -- a reference -- not its values.
0
function baz {
  set $1[@]
  printf '%s\n' "${!1}"
}
foo=(a b 'c c')
bar=("${foo[@]}")
baz bar

Comments

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.