Use a set of variables that start with the same string in bash - 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

Related

Using a string value with the name of an existing variable to get the value of the existing variable

I am using a bash script in an azure devops pipeline where a variable is created dynamically from one of the pipeline tasks.
I need to use the variable's value in subsequent scripts, I can formulate the string which is used for the variable name, however cannot get the value of the variable using this string. I hope the below example makes it clear on what I need.
Thanks in advance for your help.
PartAPartB="This is my text"
echo "$PartAPartB" #Shows "This is my text" as expected
#HOW DO I GET BELOW TO PRINT "This is my text"
#without using the PartAPartB variable and
#using VarAB value to become the variable name
VarAB="PartAPartB"
VARNAME="$VarAB"
echo $("$VARNAME") #INCORRECT
You can use eval to do this
$ PartAPartB="This is my text"
$ VarAB="PartAPartB"
$ eval "echo \${${VarAB}}"
This is my text
You have two choices with bash. Using a nameref with declare -n (which is the preferred approach for Bash >= 4.26) or using variable indirection. In your case, examples of both would be:
#!/bin/bash
VarAB="PartAPartB"
## using a nameref
declare -n VARNAME=VarAB
echo "$VARNAME" # CORRECT
## using indirection
othervar=VarAB
echo "${!othervar}" # Also Correct
(note: do not use ALLCAPS variable names, those a generally reserved for environment variables or system variables)

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

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

How can iterate thru enumerated variables in bash?

I have a set of variables, which I obtained thru eval a file.
My variables are named with this pattern:
variable_name_1
variable_name_2
...
variable_name_n
usually those variables contain a filename, so naturally I want to iterate with in this nature:
for cur in variable_name_[i]; do
<do stuff>; done
Is there a way to achieve that functionality?
Yes, using parameter expansion:
#!/bin/bash
variable_name_1="one"
variable_name_2="two"
variable_name_3="three"
for cur in ${!variable_name_*}; do
echo "${cur}=${!cur}"
done
Example run:
$ ./foo.sh
variable_name_1=one
variable_name_2=two
variable_name_3=three
But you might want to reconsider how to obtain those variables, evaling your "config file" (?) is probably not the best choice.

Bash script execute shell command with Bash variable as argument

I have one loop that creates a group of variables like DISK1, DISK2... where the number at the end of the variable name gets created by the loop and then loaded with a path to a device name. Now I want to use those variables in another loop to execute a shell command, but the variable doesn't give its contents to the shell command.
for (( counter=1 ; counter<=devcount ; counter++))
do
TEMP="\$DISK$counter"
# $TEMP should hold the variable name of the disk, which holds the device name
# TEMP was only for testing, but still has same problem as $DISK$counter
eval echo $TEMP #This echos correctly
STATD$counter=$(eval "smartctl -H -l error \$DISK$counter" | grep -v "5.41" | grep -v "Joe")
eval echo \$STATD$counter
done
Don't use eval ever, except maybe if there is no other way AND you really know what you are doing.
The STATD$counter=$(...) should give an error. That's not a valid assignment because the string "STATD$counter" is not a valid variable name. What will happen is (using a concrete example, if counter happened to be 3 and your pipeline in the $( ) output "output", bash will only expand that line as far as "STATD3=output" so it will try to find a command named "STATD3=output" and run it. Odds are this is not what you intended.
It sounds like everything you want to do can be accomplished with arrays instead. If you are not familiar with bash arrays take a look at Greg's Wiki, in particular this page or the bash man page to find out how to use them.
For example, in the loop you didn't post in your question: make disk (not DISK: don't use all upper case variable names) an array like so
disk+=( "new value" )
or even
disk[counter]="new value"
Then in the loop in your question, you can make statd an array as well and assign it with values from disk by
statd[counter]="... ${disk[counter]} ..."
It's worth saying again: avoid using eval.

Resources