2

I would like to pass multiple named arrays to another bash script as they are. For example, given outer.sh

echo "outer"
a=(1 2)
b=(3 4 5)
echo ${#a[@]}
echo ${#b[@]}
a=${a[@]} b=${b[@]} sh inner.sh

and inner.sh

echo "inner"
echo ${a[@]}
echo ${b[@]}
echo ${#a[@]}
echo ${#b[@]}

running the outer.sh gives

$ sh outer.sh
outer
2
3
inner
1 2
3 4 5
1
1

That is, even though values are preserved, but their lengths change, which means, they are not arrays anymore, but strings.

How do I pass multiple named arrays to another bash script as they are?

7
  • Through the environment, it simply can't be done without serializing and deserializing. Environment variables are NUL-delimited, and so are individual array elements, so the boundary between two elements in an array would become the boundary ending an environment variable if you tried to pass it literally. Commented Nov 29, 2018 at 2:57
  • ...that said, the serialization/deserialization necessary is very much akin to how you'd copy arrays' contents to/from a command-line argument vector, and we already have some Q&A entries describing how to do that. Commented Nov 29, 2018 at 2:58
  • BTW, sh doesn't support arrays at all. Maybe you wanted bash inner.sh? Commented Nov 29, 2018 at 2:59
  • (Not that using .sh extensions for bash scripts is great practice: Using a .sh extension implies that your code is POSIX sh compliant, which it isn't if it uses arrays; and also means that if you rewrite it to use a different interpreter, you need to rename it and then modify every single caller unless you want the name to be misleading. If a script is meant to be executed rather than sourced, better to use no extension at all, and let its shebang be used to a select an interpreter). Commented Nov 29, 2018 at 3:08
  • Is writing a_arr=($a) in inner.sh deserializing that you commented? Commented Nov 29, 2018 at 3:09

1 Answer 1

3

There are several approaches available, each with their own disadvantages.


The Easy Way: Run The Inner Script Inside A Subshell

This means all variables are inherited, including ones (like arrays!) that can't be passed through the environment.

a=(1 2)
b=(3 4 5)
(. inner)

Of course, it also means that shell settings (IFS, set -e, etc) are inherited too, so inner.sh needs to be written robustly to handle whatever setup it may happen to receive; and you can't rewrite it in a different / non-shell language later.


The Unsafe Way: Pass eval-able code (and trust your caller!)

Modify inner.sh to run [[ $setup ]] && eval "$setup", and then invoke it as:

setup=$(declare -p a b) ./inner

Obviously, this is a severe security risk if you don't control the process environment.


The Hard Way: Deserialize into individual elements

Here, we pass each array as its name, its length, and then its original elements.

inner needs to be modified to copy items off its command-argument list back into the arrays, as in the following example:

while (( $# )); do               # iterating over our argument list:
  dest_name=$1; shift            # expect the variable name first
  dest_size=$1; shift            # then its size
  declare -g -a "$dest_name=( )" # initialize our received variable as empty
  declare -n dest="$dest_name"   # bash 4.3: make "dest" point to our target name
  while (( dest_size )) && (( $# )); do  # and then, for up to "size" arguments...
    dest+=( "$1" ); shift                # pop an argument off the list onto an array
    (( dest_size -= 1 ))                 # and decrease the count left in "size"
  done
  unset -n dest   # end that redirection created above
done

...and then expand into that format in outer:

./inner a "${#a[@]}" "${a[@]}" b "${#b[@]}" "${b[@]}"
Sign up to request clarification or add additional context in comments.

2 Comments

"easy way" seems fairly robust, but i'm a bit of bash newb
It's good enough for most use cases, so long as your scripts are developed together and you know nobody will ever want to rewrite the inner one in, say, Python or Go. "The Hard Way" is the only approach that's fully language-agnostic (you can receive arrays passed that way in any language you might want to)... though one could also maybe use a language-agnostic serialization format like serializing to JSON with jq if that were an explicit goal.

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.