I don’t understand why I can’t use "${!colors[@]}" outside of the function. It works from within the function, but not outside of it in the case statement. As I understand, a variable in a function is global unless marked as local.
Any suggestions are appreciated.
#!/bin/bash
r_colors() {
declare -A colors
colors[black]="0"
colors[brown]="1"
colors[red]="2"
colors[orange]="3"
colors[yellow]="4"
colors[green]="5"
colors[blue]="6"
colors[violet]="7"
colors[grey]="8"
colors[white]="9"
#set -x
echo "Please enter a color: "
read -r user_color_input
for (( i=0; i <= ${#colors[@]}; i++ ))
do
if [[ $user_color_input == ${!colors[$i]} ]]
then
break
fi
done
echo "${colors[$user_color_input]}"
echo "${!colors[@]}" #<-test
} #<-end of r_colors
##########################################################################################
clear
echo "Please enter 1 for color value, or enter 2 for a list of colors: "
read -r user_menu_input
case "$user_menu_input" in
1) r_colors
;;
2) echo "${!colors[@]}" #<-this prints nothing
;;
*) echo "You made an invalid choice, please try again"
;;
esac
As far as I understand, there is no way to call a particular element of a regular function, outside of that function. It’s all or nothing.
But would that not be the case if that element was global? In this case, an Associative array?
I am just trying to display the result of "${!colors[@]}" as option 2 of the case, but that value resides inside the function.
I could do away with the function, and put the full code in the case statement, but that would be a mess.
Thoughts?
This is what I have:
#!/bin/bash
r_colors() { #<-resistor colors
declare -gA colors
colors[black]="0"
colors[brown]="1"
colors[red]="2"
colors[orange]="3"
colors[yellow]="4"
colors[green]="5"
colors[blue]="6"
colors[violet]="7"
colors[grey]="8"
colors[white]="9"
#set -x
echo "Please enter a color: "
read -r user_color_input
for (( i=0; i <= ${#colors[@]}; i++ ))
do
if [[ $user_color_input == "${!colors[$i]}" ]]
then
break
fi
done
echo "${colors[$user_color_input]}"
echo "${!colors[@]}" #<a test
} #<-end r_colors
##########################################################################################
clear
echo "Please enter 1 for color value, or enter 2 for a list of colors: "
read -r user_menu_input
case "$user_menu_input" in
1) r_colors
;;
2) echo "${!colors[@]}"
;;
*) echo "You made an invalid choice, please try again"
;;
esac
Functions don’t have “elements”. They contain code and define variables. Neither of which can be called.
Functions can define global variables which can then be accessed outside that function.
Variable values reside in variables. Those variables can be scoped to a function or scoped globally.
You really, really should take the time to learn the basics of shell coding so you understand the basics. It would make your life a lot easier, and it would make it easier for us to help you if you understand the basics first.
» foo () { declare -gA v; v[name]=bob; }
» unset v
» foo
» declare -p v
declare -A v=([name]="bob" )
» echo "${v[name]}"
bob
» unset colors
» r_colors() { declare -gA colors; colors[black]="0"; colors[brown]="1"; }
» r_colors
» echo "${!colors[@]}"
black brown
Why would this be an exception to “it’s all or nothing”?
Refactor. You need the array in multiple cases, so don’t define it so that it only can be used in the first case. Pull the definition out of the function and just make it a variable in the global scope.
Maybe I’m misunderstanding what is meant by global. I’m declaring the associative array, colors, to be global from within the function.
I thought global meant that it would be accessible outside of the function.
So why doesn’t it work when I call it from the case statement?
Is there a way to invoke the function without it running through its own code? I only want to display "${!colors[@]}" from it.
That’s correct, as demonstrated with my above code block.
Can you demonstrate what doesn’t work? The code you run and what happens when you run it? “Doesn’t work” is not specific. Notice how I shared both code and the output, allowing you to reproduce what I did and check the outcomes match.
“Invoke” and “run” are the same thing. A function will only be invoked or run if something else (code or the user on the terminal) runs it. Functions don’t run themselves.
You need to run the function before you access the variable. Try getting rid of the read and case and just calling the function then printing the variable.
Note, a simple reproduction shouldn’t require human inputs. If it depends on human inputs, you’d want to show those inputs and the outputs. But ideally it would just run.
I know that I could call the function in its simplified form and print "${!colors[@]}" , because that’s all the simplified version does.
But is there a way to call the full function to only print "${!colors[@]}" without it running through its full code? That’s all I need option 2 of the case to do:
#!/bin/bash
r_colors() { #<-resistor colors
declare -gA colors
colors[black]="0"
colors[brown]="1"
colors[red]="2"
colors[orange]="3"
colors[yellow]="4"
colors[green]="5"
colors[blue]="6"
colors[violet]="7"
colors[grey]="8"
colors[white]="9"
#set -x
echo "Please enter a color: "
read -r user_color_input
for (( i=0; i <= ${#colors[@]}; i++ ))
do
if [[ $user_color_input == "${!colors[$i]}" ]]
then
break
fi
done
echo "${colors[$user_color_input]}"
} #<-end r_colors
The criteria for the exercise is that 1) a user can look up the value for a particular color, and 2) simply list the different colors. That is why the for loop and the read/case statement is in there.
If there’s not a way, I would have to pull the array out of the function, making it naturally global.
No. You can’t run part of a function. A function runs until it hits a return, exit or the end of the end of the code. You can’t have it magically run just part. You could add an if something; then return; fi to make it run part of the code.
The criteria for the exercise is also not to read from STDIN, which read violates