1

I want to be able to do:

Script1.sh

declare -A map=(
  ['A']=1
  ['B']=2
  ['C']=3
  ['D']=4
)

sh script2.sh ???

Script2.sh

params = ...
echo ${params['A']}

ie, access parameters by keys. I have seen related questions for normal arrays and the answer to them has been to pass the array as:

sh script2.sh "${AR[@]}"

which I believe translates to:

sh script2.sh  "${map[0]}" "${map[1]}" "${map[2]}"

But with that, I can only access the elements based on their order.

Is there a clever trick to achieve what I want? perhaps with something that passes on "A=1" "B=2" "C=3" "D=4" instead and have script2.sh parse them? or is there a neater solution?

16
  • 3
    Did you look up similar questions before - A simple search provided me 1. How to pass an associative array as argument to a function in Bash? 2. Passing associative array as argument with Bash Commented Sep 25, 2020 at 12:14
  • 6
    Note that sh doesn’t have arrays. Commented Sep 25, 2020 at 12:15
  • 3
    params = ... is invalid syntax or sh and bash. Did you check your code at shellcheck.net ? Is there a reason you're not using "shebang" lines at the top of each script to control what shell interprets your code (i.e. #!/bin/bash), rather than your current code, bash script2 ? If readers edit your Q, then there's a risk of making a change that doesn't help define your problem. Clearly defining your problem is your responsibility, right? Good luck. Commented Sep 25, 2020 at 13:27
  • 2
    We're here to help, but clearly defining your problem is your responsibility. Have patience and learn the best way to use the site (c.f. this guide for more), but if you are the one asking for help, you can't expect someone else to be responsible for guessing what you want. Stick with it. Do your part, and you will have thousands of years of user experience to guide you. Being careless and/or defensive will just make them surf away to a more interesting problem to solve. Remember, we aren't getting paid for this. Help us help you. Commented Sep 25, 2020 at 14:37
  • 2
    And it smells faintly of an XY Problem. Commented Sep 25, 2020 at 14:53

2 Answers 2

3

If you are only calling script2.sh from inside script1.sh, then all you need to do (as @markp-fuso pointed out) is source script2.sh and it will run in the current context with all the data already loaded.

If you really want it to be on the command line, then pass it as key=val and have your code in script2.sh check each of it's args for that format and set them in an associative array.

declare -A map=()
for arg in "$@"
do if [[ "$arg" =~ ^[A-Z]=[0-9]$ ]] # more complex k/v will get ugly
   then map[${arg/=?}]=${arg/?=}    # as will the assignment w/o eval
   fi
done
# And finally, just to see what got loaded -
declare -p map

$: script2.sh C=3 A=1
declare -A map=([A]="1" [C]="3" )

As mentioned above, a more complicated possible set of key names and/or values will require a suitably more complex test as well as assignment logic. Clearly, for anything but the simplest cases, this is going to quickly get problematic.

Even better, set up a full getopts loop, and pass your args with proper indicators. This takes more design and more implementation, but that's what it takes to get more functionality.

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

3 Comments

depending on what else is in script2 it may also be possible to just source script2 (with no command line args)
You can't export an array, not usefully, anyway.
[adding for OPs benefit] re: sourcing script2 ... would (obviously) need to ensure no exit calls in script2 otherwise the exit would be invoked in script1 thus leading to script1 terminating before any additional processing could be performed
2

Assumptions:

  • the array is the only item being passed to script2 (this could be relaxed but would likely require adding some option flag processing to script2)
  • the array name will always be map (could probably make this dynamic but that's for another day)
  • the array indices and values do not contain any special/control characters (eg, line feeds) otherwise passing the array structure on the command line to script2 gets mucho complicated really quick (there are likely some workarounds for this scenario, too)

Some basic components:

The array named map:

$ declare -A map=(
  ['A']=1
  ['B']=2
  ['C']=3
  ['D']=4
)

Use typeset to generate a command to (re)produce the contents of array map:

$ typeset -p map
declare -A map=([A]="1" [B]="2" [C]="3" [D]="4" )

From here we can pass the typeset output to script2 and then have script2 evaluate the input, eg:

$ cat script1
echo "in script1"
declare -A map=(
  ['A']=1
  ['B']=2
  ['C']=3
  ['D']=4
)
./script2 $(typeset -p map)

$ cat script2
echo "in script2"
echo " \$@ = $@"
eval "$@"
for i in "${!map[@]}"
do
        echo "${i} : ${map[${i}]}"
done

Running script1 generates:

$ ./script1
in script1
in script2
 $@ = declare -A map=([A]="1" [B]="2" [C]="3" [D]="4" )
A : 1
B : 2
C : 3
D : 4

I know, I know, I know ... eval == evil. I'll have to think about a replacement for eval ... also open to suggestions.

3 Comments

I prefer to write the output from the typeset/declare to a separate file and source it. There are advantages and disadvantages. This should work well, though.
yeah, I was thinking of that (typeset -p > file; feed file to script2) as an extended answer (especially) if the array indices or values contain special/control characters; several ways to slice-n-dice this one ...
If you put double quotes like ./script2 "$(typeset -p map)", I think eval and source are exactly equivalent. Can you see any differences ?

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.