associative array gives no output [duplicate] - bash

This question already has an answer here:
Bash variables: case sensitive or not?
(1 answer)
Closed 2 years ago.
I have tried messing around with a script that sets my display resolutions as i use my laptop with different setups of external monitors. also to learn bash i suppose.
now, i have a associative array with all my monitors configured as keys, and expected resolution as value.
declare -A known_monitor
known_monitor[Virtual1]=1920x1200
known_monitor[Virtual2]=1400x1050
Known_monitor[eDP-1]=2560x1440
when i try to access the monitors like this:
for monitor in "${monitors[#]}"
do
echo ------------------
echo $monitor
echo ${known_monitor[$monitor]}
echo ------------------
done
I see the $monitor value, but the "known_monitor" is empty.
------------------
eDP-1
------------------
I have tried moving parantheses around and adding citation marks, but nothing seems to work.
Any advice would be helpful, Thanks in advance!

Corrected a few errors in your script and it works:
#!/usr/bin/env bash
declare -A known_monitor
known_monitor[Virtual1]=1920x1200
known_monitor[Virtual2]=1400x1050
known_monitor[eDP-1]=2560x1440
for monitor in "${!known_monitor[#]}"
do
echo ------------------
echo "$monitor"
echo "${known_monitor[$monitor]}"
echo ------------------
done
Alternate declaration for the known_monitor associative array:
declare -A known_monitor=(
[Virtual1]=1920x1200
[Virtual2]=1400x1050
[eDP-1]=2560x1440
)
What went wrong with your initial script:
Problem1:
Known_monitor[eDP-1]=2560x1440 with an upper-case K refers to a different variable name as in shell, variable names are case-sensitive. This one has not been declared as an associative array.
Problem2:
for monitor in "${monitors[#]}" would iterate the values of a monitors array. But nowhere in your script you have defined and populated this monitors array. You have an associative array named known_monitor.
When you iterate the values of an array, you cannot index this array by its values. You need to iterate the index or keys of the array by prepending the name of the array with an exclamation mark !. Like this: for monitor in "${!monitors[#]}"

Your last known_monitor uses a capital key, it shouldn't :
declare -A known_monitor
known_monitor[Virtual1]=1920x1200
known_monitor[Virtual2]=1400x1050
known_monitor[eDP-1]=2560x1440
monitors=(Virtual1 Virtual2 eDP-1)
for monitor in "${monitors[#]}"
do
echo ------------------
echo $monitor
echo ${known_monitor[$monitor]}
echo ------------------
done

Related

Use a set of variables that start with the same string in bash

I know something like this is possible with DOS but I am not sure how to do it within bash.
I am writing a script that takes some configuration data: source, name, and destination. There will be a variable number of these in the configuration. I need to iterate over each set.
So, for example:
#!/bin/bash
FOLDER_1_SOURCE="/path/one"
FOLDER_1_NAME="one"
FOLDER_1_DESTINATION="one"
FOLDER_2_SOURCE="/path/two two"
FOLDER_2_NAME="two"
FOLDER_2_DESTINATION="here"
FOLDER_3_SOURCE="/something/random"
FOLDER_3_NAME="bravo"
FOLDER_3_DESTINATION="there"
FOLDER_..._SOURCE="/something/random"
FOLDER_..._NAME="bravo"
FOLDER_..._DESTINATION=""
FOLDER_X_SOURCE="/something/random"
FOLDER_X_NAME="bravo"
FOLDER_X_DESTINATION=""
Then I want to iterate over each set and get the SOURCE and NAME values for each set.
I am not stuck on this format. I just don't know how else to do this. The end goal is that I have 1 or more set of variables with source, name, and destination and then I need to iterate over them.
The answer to this type of question is nearly always "use arrays".
declare -a folder_source folder_name folder_dest
folder_source[1]="/path/one"
folder_name[1]="one"
folder_dest[1]="one"
folder_source[2]="/path/two two"
folder_name[2]="two"
folder_dest[2]="here"
folder_source[3]="/something/random"
folder_name[3]="bravo"
folder_dest[3]="there"
folder_source[4]="/something/random"
folder_name[4]="bravo"
folder_dest[4]=""
for((i=1; i<=${#folder_source[#]}; ++i)); do
echo "$i source:" "${folder_source[$i]}"
echo "$i name:" "${folder_name[$i]}"
echo "$i destination:" "${folder_dest[$i]}"
done
Demo: https://ideone.com/gZn0wH
Bash array indices are zero-based, but we just leave the zeroth slot unused here for convenience.
Tangentially, avoid upper case for your private variables.
AFIK bash does not have a facility to list all variables. A workaround - which also would mimic what is going on in DOS - is to use environment variables and restrict your search to those. In this case, you could do a
printenv|grep ^FOLDER||cut -d = -f 1
This is the same as doing in Windows CMD shell a
SET FOLDER

Bash - Add variable in associative array - bad substitution [duplicate]

This question already has answers here:
Bash indirect variable referencing
(1 answer)
Bash indirect array addressing?
(3 answers)
Closed 2 years ago.
Here is an expected behavior for associated array in bash
$ declare -A PC=( [Monitor]=Dell [CPU]=HP )
$ echo ${PC[CPU]}
HP
This gives me HP as output
Lets say I have these PC,Monitor amd CPU values stored in variable a , b and c. I am trying fetch the details now but I am getting "bad substitution" error when trying so.
$ a=PC; b=Dell; c=HP
$ echo ${$a[$b]}
bash: ${$a[$b]}: bad substitution
$ echo ${PC[$b]}
Dell
${PC[$b]} however is returning expected output but not {$a[$b]}
Not sure how this can be achieved. Thanks in advance.
What you are trying to do is called indirection - using one variable as the name of another variable.
In bash you do this for normal variables using the syntax ${!var}, as in
a=5
b=a
echo ${!b} # 5
Unfortunately this won't work how you want for an array variable because the syntax ${!array[*]} means something else (getting all keys from an associative array).
Instead, as suggested by a comment below, you can create a string for the entire reference and then use redirection on that:
lookup="$a[$b]"
echo ${!lookup} # will give Dell in your example

How to iterate through a set of variables, then expand the variables in Bash

I'm trying to set up a "check if machines are online" script with Bash, but running into an issue of when and where to define the variables so they're expanded properly. Something like:
#!/bin/bash
rm01="c01 c02 c03"
rm02="d01 d02 d03"
rm10="e11 e22 e33"
for room in rm01 rm02 rm03; do
echo $room
for computer in $room; do
#run various nslookup/ping tests and report
done
done
exit 0
I'm running into issues because I can't find a way to expand $room for its corresponding set of computers (in $rm01, $rm02, $rm10) listed at the beginning.
What am I doing wrong?
The quick fix is to use variable indirection:
for computer in ${!room}; do
Relying on word splitting is rarely the best idea, though. You could use arrays and namerefs instead (requires Bash 4.3 or newer):
#!/usr/bin/env bash
# Declare arrays
rm01=(c01 c02 c03)
rm02=(d01 d02 d03)
rm03=(e11 e22 e33)
# Declare room as nameref
declare -n room
# Using nameref as control variable sets room as reference to each variable in turn
for room in rm{01..03}; do
# Properly quoted array expansion
for computer in "${room[#]}"; do
echo "$computer" # or whatever needs to be done
done
done
exit 0

create multiple global variables from a loop in bash [duplicate]

This question already has answers here:
Indirect variable assignment in bash
(7 answers)
Dynamic variable names in Bash
(19 answers)
Closed 4 years ago.
So I have a loop that basically goes through all of the disks installed on the system, then it assigns a variable to a disk name. however, I cannot use those variables if it's not inside the loop, how do I make them available to be used in other functions or other parts of the script?
Here is the code
#!/bin/bash
dev=1
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
set "DISK$dev=$disk"
dev=$((dev+1))
done
So if I do echo $DISK1 for example, it doesn't display anything.
But if I do echo $DISK1 INSIDE the loop, then ir displays the fist variable assignment. Can I export them and make them available outside of the loop?
set is not at all the right command to use here. You could pull this off with eval where you have set; but the proper way to solve this is simply to assign the values to an array.
disks=($(fdisk -l | grep -o '/dev/sd[a-z]'))
You can loop over the individual entries with ${disk[0]} through ${disk[n]} or retrieve the entire array at once with "${disk[#]}". The expression ${#disk[#]} evaluates to the number of elements in the array; though because array indexing is zero-based, the last index is one less than this value.
Of course, very often, you don't really need to keep the results in a variable explicitly. If you don't need random access (do you need to recall what you know about the first disk when processing the fifth? Really?) you should probably just loop over the values directly.
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
: whatever you need to do with the current "$disk"
done
You cannot directly access the parent scope. Also with "export" you will be able to access the exported variable to sub-shell but not the parent one.
A work around for this can be put all these set into a file (appending the new sets) and execute it with after the loop:
dev=1
for disk in $(fdisk -l | grep -o '/dev/sd[a-z]'); do
echo "export \"DISK$dev=$disk\";" >> my_script_full_of_sets.sh
dev=$((dev+1))
done
. my_script_full_of_sets.sh

evaluating variable that comes in an array in shell script

I am trying to read Xcode build settings via shell script, i.e. if there is a build setting called PRODUCT_NAME, I read its value by writing echo ${PRODUCT_NAME} in shell script.
Now, I get this PRODUCT_NAME in an array, lets call myArr having just one element PRODUCT_NAME. I loop over that array as
for i in "${myarr[#]}"
do
:
echo $i
done
echo $i would simply output PRODUCT_NAME. However, I want to write something that would evaluate ${PRODUCT_NAME} and give me results.
I have also tried eval echo $i but that also outputs PRODUCT_NAME only
Solved it by using
echo "${!i}"
The second line gives the output as desired. I have taken this answer from here: How to get a variable value if variable name is stored as string?
As I interpret your problem, You hold the array of variables, and while traversing, you want to use the actual values of the variables.
I tried the below code which is almost similar to which you have written and seems working.
#!/bin/bash
PRODUCT_NAME="Calendar"
PROCUCT_VER="2.3"
PROCUCT_OWNER="Sam"
PRODUCT_DETAILS=( $PRODUCT_NAME $PROCUCT_VER $PROCUCT_OWNER )
for PRODUCT_PROPERTY in "${PRODUCT_DETAILS[#]}"
do
echo "PRODUCT_PROPERTY: $PRODUCT_PROPERTY"
done
Output:
PRODUCT_PROPERTY: Calendar
PRODUCT_PROPERTY: 2.3
PRODUCT_PROPERTY: Sam

Resources