shell script declare associative variables from array - bash

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

Related

How to assign value to a variable that is provided from file in for loop

Im facing a problem with assigning a value to a variable that its name is stored in other variable or a file
cat ids.txt
ID1
ID2
ID3
What i want to do is:
for i in `cat ids.txt'; do $i=`cat /proc/sys/kernel/random/uuid`
or
for i in ID1 ID2 ID3; do $i=`cat /proc/sys/kernel/random/uuid`
But its not working.
What i would like to have, its something like:
echo $ID1
5dcteeee-6abb-4agg-86bb-948593020451
echo $ID2
5dcteeee-6abb-4agg-46db-948593322990
echo $ID3
5dcteeee-6abb-4agg-86cb-948593abcd45
Use declare. https://linuxcommand.org/lc3_man_pages/declareh.html
# declare values
for i in ID1 ID2 ID3; do
declare ${i}=$(cat /proc/sys/kernel/random/uuid)
done
# read values (note the `!` in the variable to simulate "$ID1", not ID1)
for i in ID1 ID2 ID3; do echo ${!i}; done
3f204128-bac6-481e-abd3-37bb6cb522da
ccddd0fb-1b6c-492e-bda3-f976ca62d946
ff5e04b9-2e51-4dac-be41-4c56cfbce22e
Or better yet... Reading IDs from the file:
for i in $(cat ids.txt); do
echo "ID from file: ${i}"
declare ${i}=$(cat /proc/sys/kernel/random/uuid)
echo "${i}=${!i}"
done
Result:
$ cat ids.txt
ID1
ID2
ID3
$ for i in $(cat ids.txt); do echo "ID from file: ${i}"; declare ${i}=$(cat /proc/sys/kernel/random/uuid); echo "${i}=${!i}"; done
ID from file: ID1
ID1=d5c4a002-9039-498b-930f-0aab488eb6da
ID from file: ID2
ID2=a77f6c01-7170-4f4f-a924-1069e48e93db
ID from file: ID3
ID3=bafe8bb2-98e6-40fa-9fb2-0bcfd4b69fad
A one-liner using . built-in, process and command substitution, and printf's implicit loop:
. <(printf '%s=$(cat /proc/sys/kernel/random/uuid)\n' $(<ids.txt))
echo "ID1=$ID1"; echo "ID2=$ID2"; echo "ID3=$ID3"
Note: The lines of ids.txt must consist of only valid variable names and the file must come from a trusted source. Checking that file by grep -vq '^[[:alpha:]][[:alnum:]]*$' ids.txt before calling this command may be a safer approach.
Method with associative array:
#!/usr/bin/env bash
# Declares associative array to store ids and UUIDs
declare -A id_map=()
# Reads ids.txt into array
mapfile -t ids < ids.txt
# Iterates ids
for id in "${ids[#]}"; do
# Populates id_map with uuid for each id
# Prepends $id with an x because associative array keys must not be empty
read -r id_map["x$id"] < /proc/sys/kernel/random/uuid
done
# Debug content of id_map
for x_id in "${!id_map[#]}"; do
id="${x_id#?}" # Trims leading x filler
printf '%s=%s\n' "$id" "${id_map[$x_id]}"
done

Split String by Double Back Slashes in Bash

I am trying to split the string by double back slashes in bash and somehow it's not working.
My string is as follow:
abc\\xyz
My Code:
my_str='abc\\xyz'
IFS='\\' read -r -a arr <<< "$my_str"
echo "${#arr[#]}"
I am expecting that the output would be '2' as the length of the array would be 2.
However, I get 3 as a result and when I try to print the array values, I only get 'abc', and the second index remains empty. It seems like something is wrong with my code but I am unable to identify it.
Can anyone please help to resolve the issue?
If there are no spaces in your string you could use bash pattern substitutions to replace pairs of backslashes by a space and assign the result to an indexed array:
$ my_str='abc\\xyz\uvw\\rst\\\012\\\\345'
$ declare -a arr=( ${my_str//\\\\/ } )
$ echo "${#arr[#]}"
5
$ printf '%s\n' "${arr[#]}"
abc
xyz\uvw
rst
\012
345
Perhaps you could try to replace the backslashes on the string fist as showcased in this previous question. However, this would be inefficient for very large strings.
A slightly different take -
$: my_str='abc\\123\\ghi\\\012\\jkl\\foo bar\\\\xyz'
$: IFS='|' read -r -a arr <<< "${my_str//\\\\/\|}"
$: printf "[%s]\n" "${arr[#]}"
[abc]
[123]
[ghi]
[\012]
[jkl]
[foo bar]
[]
[xyz]

Display First and Last string entries stored in a variable

I have a variable MyVar with values stored in it. For example:
MyVar="123, 234, 345, 456"
Each entry in the variable is separated by a coma as in the example above.
I want to be able to pick the first and last entry from this variable, i.e 123 and 456 respectively.
Any idea how I can achieve this from the command prompt terminal ?
Thanks!
Using bash substring removal:
$ echo ${MyVar##*,}
456
$ echo ${MyVar%%,*}
123
Also:
$ echo ${MyVar/,*,/,}
123, 456
More for example here:
https://tldp.org/LDP/abs/html/parameter-substitution.html
Edit: Above kind of expects the substrings to be separated by commas only. See comments where #costaparas gloriously demonstrates a case with , .
Try using sed:
MyVar="123, 234, 345, 456"
first=$(echo "$MyVar" | sed 's/,.*//')
last=$(echo "$MyVar" | sed 's/.*, //')
echo $first $last
Explanation:
To obtain the first string, we replace everything after & including
the first comma with nothing (empty string).
To obtain the last string, we replace everything before & including the last comma with nothing (empty string).
Using bash array:
IFS=', ' arr=($MyVar)
echo ${arr[0]} ${arr[-1]}
Where ${arr[0]} and ${arr[-1]} are your first and last respective values. Negative index requires bash 4.2 or later.
You could try following also with latest BASH version, by sending variable values into an array and then retrieve first and last element, keeping all either values in it saved in case you need them later in program etc.
IFS=', ' read -r -a array <<< "$MyVar"
echo "${array[0]}"
123
echo "${array[-1]}"
456
Awk alternative:
awk -F "(, )" '{ print $1" - "$NF }' <<< $MyVar
Set the field separator to command and a space. Print the first field and the last field (NF) with " - " in between.

Create a loop for 3 different variables to output all possible combinations

So lets say i have 3 lines of code
ABC
123
!##
How do i create a for loop to output the number of ways to piece them together?
E.G ABC123!##, ABC!##123, 123ABC!##$
here is my current line of code
#!/bin/bash
alphabet='ABC' numbers='123' special='!##'
for name in $alphabet$numbers$special
do
echo $name
done
echo done
alphabet='ABC' numbers='123' special='!##'
for name1 in $alphabet $numbers $special
#on 1st iteration, name1's value will be ABC, 2nd 123 ...
do
for name2 in $alphabet $numbers $special
do
for name3 in $alphabet $numbers $special
do
#here we ensure that we want strings only as combination of that three strings
if [ $name1 != $name2 -a $name2 != $name3 ]
then
echo $name1$name2$name3
fi
done
done
done
if you want also to print strings, like 123123123 and ABCABCABC, remove if condition
You can also do it without a loop at all using brace expansion (but you lose the ability to exclude, e.g. ABCABCABC). For example:
#!/bin/bash
alpha='ABC'
num='123'
spec='!##'
printf "%s\n" {$alpha,$num,$spec}{$alpha,$num,$spec}{$alpha,$num,$spec}
Example Use/Output
$ bash permute_brace_exp.sh
ABCABCABC
ABCABC123
ABCABC!##
ABC123ABC
ABC123123
ABC123!##
ABC!##ABC
ABC!##123
ABC!##!##
123ABCABC
123ABC123
123ABC!##
123123ABC
123123123
123123!##
123!##ABC
123!##123
123!##!##
!##ABCABC
!##ABC123
!##ABC!##
!##123ABC
!##123123
!##123!##
!##!##ABC
!##!##123
!##!##!##

How to get the array name from a variable

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

Resources