0

I am trying to extract the value from a bash array. It works fine if it is the following code -

### stk3.sh 
#!bin/bash
declare -x -A my_ids
my_ids[cat]=100
my_ids[dog]=200
id_key=${1:-cat}  ## setting default
echo "Id key is :${id_key}"
id_no=${my_ids[$id_key]}
echo "Value is ### ${id_no}"

if I execute the above I get the desired output - (saved the above script in stk3.sh)

./stk3.sh dog
       Id key is :dog
       Value is ### 200

Now, when I use functions, I am unable to get the desired output. Defined the function in one script (stk1.sh) and calling the function to get the value from a specific key from another script (stk2.sh)

### stk1.sh
#!/bin/bash
declare -x -A my_ids
function my_ids {
  my_ids[cat]=100
  my_ids[dog]=200
  echo "Length of Array :${#my_ids[*]}"
  echo "*** Now printing the contents of the Array ...."
  for x in "${!my_ids[@]}"
  do
    printf "$x  ${my_ids[$x]} \n"
  done
}
my_ids


function get-value {
  printf "$1\n"
  if [[ -n "$1" ]]
    then
    id_no=${my_ids[$id_key]}
  fi
}

When calling the function using, I am NOT getting the desired results- Here is my stk2.sh

### stk2.sh 
#!/bin/bash
 
source stk1.sh
id_key=${1:-cat}  ## setting default
echo "Id key is :${id_key}"
id_no=$(get-value $id_key)
echo "Value is ### ${id_no}"

Output after executing skt2.sh, As you can see it is not returning the value of id _no, which in this case should be 200:

./stk2.sh dog
Length of Array :2
*** Now printing the contents of the Array ....
dog  200 
cat  100 
Id key is :dog
Value is ### dog
CloudWatcher
  • 181
  • 1
  • 11
Judi
  • 710
  • 3
  • 10
  • 25
  • `get-value` is missing `}`? `As you can see it is not` I cannot see that - the output has `Value is ### dog` – KamilCuk Jul 14 '23 at 13:15
  • I think the missing `}` is a copy&paste error, when I add it, the scripts run as described. I have proposed an edit that fixes that and clears up what (I assume) is the expected outcome. – CloudWatcher Jul 14 '23 at 13:34
  • While assignments in a function modify global variables by default, it's a *different* global variable in a command substitution than if you execute the function directly in the current shell. (I.e., `get-value $id_key` and `$(get-value $id_key)` assign to different `id_no` values.) – chepner Jul 14 '23 at 13:44
  • Arrays can't be exported; you don't get the effect of `-x` when you use `declare -A`. – Charles Duffy Jul 15 '23 at 12:41

4 Answers4

2

id_no is set within the get-value() function call, and since you haven't defined id_no as 'local' to the function, the new value of id_no is automatically available to the calling/parent process.

What you want to do is call the function without trying to capture its output, eg:

$ cat stk2.sh 
#!/bin/bash

source stk1.sh
id_key=${1:-cat}  ## setting default
echo "Id key is :${id_key}"
get-value $id_key
echo "Value is ### ${id_no}"
markp-fuso
  • 28,790
  • 4
  • 16
  • 36
0

What you are trying to do is not directly possible. You can't return something that is not an exit code from a function in bash. This answer discusses alternatives.

Your line id_no=$(get-value $id_key) in stk2.sh captures all output from that command (which you start in a subshell by using $(...)), which in this case is determined by the line printf "$1\n" in stk1.sh.

Here is another insightful discussion about return values of bash functions etc.

CloudWatcher
  • 181
  • 1
  • 11
0

get-value assigns the value of id_no and outputs the input. But then your main script assigns id_no to hold the output of get-value, which is the key you pass in. Not the behaviour you're looking for.

Try

function get-value {
    printf '%s\n' "${my_ids[$1]:?"no such key"}"
}

${var:?error-message} emits the error message if var is null or unset and produces an error status. See https://www.gnu.org/software/bash/manual/bash.html#Shell-Parameter-Expansion

Then the main script can have

if ! id_no=$(get-value "$id_key"); then
    echo "try a different key"
    exit 1
fi
do-something-with "$id_no"
glenn jackman
  • 238,783
  • 38
  • 220
  • 352
0

Your get-value function ...

function get-value {
  printf "$1\n"
  if [[ -n "$1" ]]
    then
    id_no=${my_ids[$id_key]}
  fi
}

... prints its first argument ("$1"), then, if the latter is non-empty, assigns the value of my_ids[$id_key] to variable id_no.

Which raises a few questions :

  1. why does your get-value function manipulates variables which were not passed as arguments? get-value is invoked inside a command substitution statement : it runs in a subshell, with a copy of the parent environment. Sure, it will be able to access id_key's value, because id_key happened to be defined earlier in stk2.sh, but then what's the use of checking whether first function argument ("$1") is empty or not?

  2. your assignment statement concerning id_no will not be reflected in the parent environment. It will exist only during the subshell run of get-value.

  3. you're misusing the command substitution feature. $(get-value $id_key) will be replaced by whatever gets printed to STDOUT during the subshell run. Since your function does printf "$1\n", it actually returns (as the substituted string) the value of its first argument. So if id_key is "cat", $(get-value $id_key) returns "cat".


To sum it up (assuming stk2.sh invocation as ./stk2.sh dog:

id_key=${1:-cat}  ## setting default     id_key is set to "dog"

id_no=$(get-value $id_key)               within subshell:
                                            * print "dog\n"
                                            * id_no=200
                                         back to initial environment:
                                            * id_no="dog"

echo "Value is ### ${id_no}"             prints "Value is ### dog"

Simple suggestion:

function get-value {
  if [[ -n "$1" ]]
    then
      echo ${my_ids[$1]}
  fi  
}

Hope that helps.

Grobu
  • 599
  • 1
  • 11