How to get the array name from a variable - shell

How to get the array name from the below ?
Getting the name of the array from config :
jobcfgUniqName=`echo ${config_data} | awk -F "#" '{print $3}'`
Creating an array of it :
for ((xx = 0; xx <= ${#joblognameSearch[#]}; xx++))
do
print $joblognameSearch[$xx]
eval ($jobcfgUniqName)[$xx]=`grep -B 3 -i error $joblogPath/$joblognameSearch[$xx]`
print jobcfgUniqName : ${jobcfgUniqName}
done
This line I tried changing many ways but did not work :
eval ($jobcfgUniqName)[$xx]

You can use declare bulletin of BASH to replace your eval by this:
declare arr_"$jobcfgUniqName"[$xx]=`grep -B 3 -i error $joblogPath/$joblognameSearch[$xx]`
Now you will have dynamic array create with prefix arr_ and some variable name $jobcfgUniqName.
TESTING:
# set the array
s='abc'
declare arr_"$s"[0]='foo'
declare arr_"$s"[1]='bar'
# retrieve the values
v1=arr_"$s"[0]
v2=arr_"$s"[1]
echo "${!v1}"
foo
echo "${!v2}"
bar

Add echo.
Example:
#!/bin/bash
A="abcd dcba"
B=A
C='eval "echo \$$B"'
eval "$C"
$ bash 1.sh
abcd dcba

Related

How do i add whitepsaces to a String while filling it up in a for-loop in Bash?

Have a string as follows:
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
Want to extract the first two directories and save it as another string like this:
"applications/dbt applications/dbt applications/dataform pplications/dataform"
But while filling up the second string, its being saved as
applications/dbtapplications/dbtapplications/dataformapplications/dataform
What i tried:
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
arr=($files)
#extracting the first two directories and saving it to a new string
for i in ${arr[#]}; do files2+=$(echo "$i" | cut -d/ -f 1-2); done
echo $files2
files2 echoes the following
applications/dbtapplications/dbtapplications/dataformapplications/dataform
Reusing your code as much as possible:
(assuming to only remove the last right part):
arr=( applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml )
#extracting the first two directories and saving it to a new string
for file in "${arr[#]}"; do
files2+="${file%/*} "
done
echo "$files2"
applications/dbt applications/dbt applications/dataform
You could use a for loop as requested
for dir in ${files};
do file2+=$(printf '%s ' "${dir%/*}")
done
which will give output
$ echo "$file2"
applications/dbt applications/dbt applications/dataform applications/dataform
However, it would be much easier with sed
$ sed -E 's~([^/]*/[^/]*)[^ ]*~\1~g' <<< $files
applications/dbt applications/dbt applications/dataform applications/dataform
Convert the string in an array first. Assuming there are no white/blank/newline space embedded in your strings/path name. Something like
#!/usr/bin/env bash
files="applications/dbt/Dockerfile applications/dbt/cloudbuild.yaml applications/dataform/Dockerfile applications/dataform/cloudbuild.yaml"
mapfile -t array <<< "${files// /$'\n'}"
Now check the value of the array
declare -p array
Output
declare -a array=([0]="applications/dbt/Dockerfile" [1]="applications/dbt/cloudbuild.yaml" [2]="applications/dataform/Dockerfile" [3]="applications/dataform/cloudbuild.yaml")
Remove all the last / from the path name in the array.
new_array=("${array[#]%/*}")
Check the new value
declare -p new_array
Output
declare -a new_array=([0]="applications/dbt" [1]="applications/dbt" [2]="applications/dataform" [3]="applications/dataform")
Now the value is an array, assign it to a variable or do what ever you like with it. Like what was mentioned in the comment section. Use an array from the start.
Assign the first 2 directories/path in a variable (weird requirement)
new_var="${new_array[#]::2}"
declare -p new_var
Output
declare -- new_var="applications/dbt applications/dbt"

shell script declare associative variables from array

I have array key-value pairs separated by space,
array=Name:"John" ID:"3234" Designation:"Engineer" Age:"32" Phone:"+123 456 789"
Now I want convert above array as associative variables like below,
declare -A newmap
newmap[Name]="John"
newmap[ID]="3234"
newmap[Designation]="Engineer"
newmap[Age]="32"
newmap[Phone]="+123 456 789"
echo ${newmap[Name]}
echo ${newmap[ID]}
echo ${newmap[Designation]}
echo ${newmap[Age]}
echo ${newmap[Phone]}
I'm able to get value for given key using file,
declare -A arr
while IFS='=' read -r k v; do
arr[$k]=$v;
done < "file.txt"
echo "${arr[name]}"
But I want to implement same functionality using array instead of file.
You can just use a sed to reformat input data before calling declare -A:
s='array=Name:"John" ID:"3234" Designation:"Engineer" Age:"32" Phone:"+123 456 789"'
declare -A "newmap=(
$(sed -E 's/" ([[:alpha:]])/" [\1/g; s/:"/]="/g' <<< "[${s#*=}")
)"
Then check output:
declare -p newmap
declare -A newmap=([ID]="3234" [Designation]="Engineer" [Age]="32" [Phone]="+123 456 789" [Name]="John" )
A version without eval:
array='Name:"John" ID:"3234" Designation:"Engineer" Age:"32" Phone:"+123 456 789"'
declare -A "newmap=($(perl -pe 's/(\w+):"/[\1]="/g' <<< "$array"))"
echo ${newmap[Phone]}
# output : +123 456 789
Working with the variable array that's been defined as follows:
$ array='Name:"John" ID:"3234" Designation:"Engineer" Age:"32" Phone:"+123 456 789"'
NOTES:
assuming no white space between the attribute, ':' and value
assuming there may be variable amount of white space between attribute/value pairs
assuming all values are wrapped in a pair of double quotes
And assuming the desire is to parse this string and store in an array named newmap ...
We can use sed to break our string into separate lines as such:
$ sed 's/" /"\n/g;s/:/ /g' <<< ${array}
Name "John"
ID "3234"
Designation "Engineer"
Age "32"
Phone "+123 456 789"
We can then feed this to a while loop to populate our array:
$ unset newmap
$ typeset -A newmap
$ while read -r k v
do
newmap[${k}]=${v//\"} # strip off the double quote wrapper
done < <(sed 's/" /"\n/g;s/:/ /g' <<< ${array})
$ typeset -p newmap
declare -A newmap=([ID]="3234" [Name]="John" [Phone]="+123 456 789" [Age]="32" [Designation]="Engineer" )
And applying the proposed (and slightly modified) echo statements:
$ (
echo "Name - ${newmap[Name]}"
echo "ID - ${newmap[ID]}"
echo "Designation - ${newmap[Designation]}"
echo "Age - ${newmap[Age]}"
echo "Phone - ${newmap[Phone]}"
)
Name - John
ID - 3234
Designation - Engineer
Age - 32
Phone - +123 456 789

Set variable equal to value based on match in bash

I have a script where I want to set a variable equal to a specific value based on a match from a file (in bash).
For example:
File in .csv contains:
Name,ID,Region
Device1,1,USA
Device2,2,UK
I want to declare variables at the beginning like this:
region1=USA
regions2=UK
region3=Ireland
etc...
Then, whilst reading the csv, I need to match the Regioncolumn's name to the global variable set at the beginning of a file, to then use this in an API. So if a device in the csv has a region set of USA, I should be able to use region1 during the update call in the API. I want to use a while loop to iterate over the csv file line by line, and update each device's region.
Does anyone maybe know how I can achieve this? Any help would be greatly appreciated.
PS: This is not a homework assignment before anyone asks :-)
Would you please try the following:
declare -A regions # an associative array
# declare your variables
region1=USA
region2=UK
region3=Ireland
# associate each region's name with region's code
for i in "${!region#}"; do # expands to region1, region2, region3
varname="$i"
regions[${!varname}]="$i" # maps "USA" to "region1", "UK" to "region2" ..
done
while IFS=, read -r name id region; do
((nr++)) || continue # skips the header line
region_group="${regions[$region]}"
echo "region=$region, region_group=$region_group" # for a test
# call API here
done < file.csv
Output:
region=USA, region_group=region1
region=UK, region_group=region2
region=Ireland, region_group=region3
BTW if the variable declaration at the beginning is under your control, it will be easier to say:
# declare an associative aarray
declare -A regions=(
[USA]="region1"
[UK]="region2"
[Ireland]="region3"
)
while IFS=, read -r name id region; do
((nr++)) || continue # skip the header line
region_group="${regions[$region]}"
echo "region=$region, region_group=$region_group" # for a test
# call API here
done < file.csv
Use awk to create a lookup table. eg:
$ cat a.sh
#!/bin/sh
cat << EOD |
region1=USA
region2=UK
region3=Ireland
EOD
{
awk 'NR==FNR { lut[$2] = $1; next}
{$3 = lut[$3]} 1
' FS== /dev/fd/3 FS=, OFS=, - << EOF
Name,ID,Region
Device1,1,USA
Device2,2,UK
EOF
} 3<&0
$ ./a.sh
Name,ID,
Device1,1,region1
Device2,2,region2
or, slightly less obscure (and more portable to just use regular files, I'm not sure about the platforms on which /dev/fd/3 is actually valid:
$ cat input
Name,ID,Region
Device1,1,USA
Device2,2,UK
$ cat lut
region1=USA
region2=UK
region3=Ireland
$ awk 'NR==FNR{lut[$2] = $1; next} {$3 = lut[$3]} 1' FS== lut FS=, OFS=, input
Name,ID,
Device1,1,region1
Device2,2,region2

values to array from variable names with pattern

I have an unknown number of variable names with the pattern rundate*. For example, rundate=180618 && rundate2=180820. I know from here that I can send multiple variable names to a third variable: alld=(`echo "${!rundate*}"`) and while attempting to solve my problem, I figured out how to send multiple variable indices to a third variable: alld_indices=(`echo "${!alld[#]}"`). But, how do I send multiple values to my third variable: alld_values such that echo ${alld_values[#]} gives 180618 180820. I know from here how I can get the first value: firstd_value=(`echo "${!alld}"`). I suspect, I've seen the answer already in my searching but did not realize it. Happy to delete my question if that is the case. Thanks!
#!/usr/bin/env bash
# set up some test data
rundate="180618"
rundate1="180820"
rundate2="Values With Spaces Work Too"
# If we know all values are numeric, we can use a regular indexed array
# otherwise, the below would need to be ''declare -A alld=( )''
alld=( ) # initialize an array
for v in "${!rundate#}"; do # using # instead of * avoids IFS-related bugs
alld[${v#rundate}]=${!v} # populate the array, using varname w/o prefix as key
done
# print our results
printf 'Full array definition:\n '
declare -p alld # emits code that, if run, will redefine the array
echo; echo "Indexes only:"
printf ' - %s\n' "${!alld[#]}" # "${!varname[#]}" expands to the list of keys
echo; echo "Values only:"
printf ' - %s\n' "${alld[#]}" # "${varname[#]}" expands to the list of values
...properly emits as output:
Full array definition:
declare -a alld=([0]="180618" [1]="180820" [2]="Values With Spaces Work Too")
Indexes only:
- 0
- 1
- 2
Values only:
- 180618
- 180820
- Values With Spaces Work Too
...as you can see running at https://ideone.com/yjSD1J
eval in a loop will do it.
$: for v in ${!rundate*}
> do eval "alld_values+=( \$$v )"
> done
$: echo "${alld_values[#]}"
180618 180820
or
$: eval "alld_values=( $( sed 's/ / $/g' <<< " ${!rundate*}" ) )"
or
$: echo "alld_values=( $( sed 's/ / $/g' <<< " ${!rundate*}" ) )" > tmp && . tmp

Creating a string variable name from the value of another string

In my bash script I have two variables CONFIG_OPTION and CONFIG_VALUE which contain string VENDOR_NAME and Default_Vendor respectively.
I need to create a variable with name $CONFIG_OPTION ie VENDOR_NAME and assign the value in CONFIG_VALUE to newly created variable.
How I can do this?
I tried
$CONFIG_OPTION=$CONFIG_VALUE
But I am getting an error on this line as
'./Build.bash: line 137: VENDOR_NAME="Default_Vendor": command not found'
Thanks.
I know that nobody will mention it, so here I go. You can use printf!
#!/bin/bash
CONFIG_OPTION="VENDOR_NAME"
CONFIG_VALUE="Default_Vendor"
printf -v "$CONFIG_OPTION" "%s" "$CONFIG_VALUE"
# Don't believe me?
echo "$VENDOR_NAME"
This uses bash builtins:
#!/bin/bash
VAR1="VAR2"
declare "${VAR1}"="value"
echo "VAR1=${VAR1}"
echo "VAR2=${VAR2}"
The script output:
VAR1=VAR2
VAR2=value
Here's the snippet using your variable names:
#!/bin/bash
CONFIG_OPTION="VENDOR_NAME"
declare "${CONFIG_OPTION}"="value"
echo "CONFIG_OPTION=${CONFIG_OPTION}"
echo "VENDOR_NAME=${VENDOR_NAME}"
The script output:
CONFIG_OPTION=VENDOR_NAME
VENDOR_NAME=value
For pure shell, possibly try:
#!/usr/bin/env sh
option=vendor_name
value="my vendor"
eval $option="'$value'" # be careful with ', \n, and \ in value
eval echo "\$$option" # my vendor
echo "$vendor_name" # my vendor
Why?
#!/usr/bin/env sh
printf -v "var" "val" # prints the flag, var not set
declare var=val # sh: declare: not found
echo ${!var} # sh: syntax error: bad substitution
I don't like eval, but are there any POSIX options?
Existing answers are great for strings, but do not work with arrays.
Here is a working solution for arrays:
ARRAY=(a b c d e f)
array_name="ARRAY_NAME"
TARGET_ARRAY="${array_name}[#]" # ARRAY="ARRAY[#]"
TARGET_ARRAY=("${!TARGET_ARRAY}") # ARRAY=(a b c d e f)
the array_name string can also have * suffix to obtain the array elements.

Resources