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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.