Cat strings from file and compare it with associative bash array - arrays

I create associative array for example
declare -A vars=(["Test array here"]="POOLING TESTPOOL ")
And i have file with multiple lines which want to compare with array values and if match print array key.
I try this but it's doesn't work
match="POOLING TESTPOOL"
for key in "${vars[#]}"; do [[ $key = "$match" ]] && printf '%s\n' "${!vars[$key]}" ; done
Also I try glob
for key in "${vars[#]}"; do [[ $key = *"$match"* ]] && printf '%s\n' "${!vars[$key]}" ; done
But every time I got empty string

So if you want cat files and compare string with associative array value
and print its key you can use this nested loop
for i in `cat file.txt`; do for key in "${!vars[#]}"; do if [[ ${vars[$key]} == *"$i"* ]]; then echo $key fi done done
Thanks to #Fravadona to point me.

Related

Bash how to substitute string variables for array name [duplicate]

Let's say we declared two associative arrays:
#!/bin/bash
declare -A first
declare -A second
first=([ele]=value [elem]=valuee [element]=valueee)
second=([ele]=foo [elem]=fooo [element]=foooo)
# echo ${$1[$2]}
I want to echo the given hashmap and element from script inputs. For example, if I run sh.sh second elem, the script should echo fooo.
An inelegant but bullet-proof solution would be to white-list $1 with the allowed values:
#!/bin/bash
# ...
[[ $2 ]] || exit 1
unset result
case $1 in
first) [[ ${first["$2"]+X} ]] && result=${first["$2"]} ;;
second) [[ ${second["$2"]+X} ]] && result=${second["$2"]} ;;
*) exit 1 ;;
esac
[[ ${result+X} ]] && printf '%s\n' "$result"
notes:
[[ $2 ]] || exit 1 because bash doesn't allow empty keys
[[ ${var+X} ]] checks that the variable var is defined; with this expansion you can also check that an index or key is defined in an array.
A couple ideas come to mind:
Variable indirection expansion
Per this answer:
arr="$1[$2]" # build array reference from input fields
echo "${!arr}" # indirect reference via the ! character
For the sample call sh.sh second elem this generates:
fooo
Nameref (declare -n) (requires bash 4.3+)
declare -n arr="$1"
echo "${arr[$2]}"
For the sample call sh.sh second elem this generates:
fooo

How to create mutiple arrays from a text file and loop through the values of each array

I have a text file with the following:
Paige
Buckley
Govan
Mayer
King
Harrison
Atkins
Reinhardt
Wilson
Vaughan
Sergovia
Tarrega
My goal is to create an array for each set of names. Then Iterate through the first array of values then move on to the second array of values and lastly the third array. Each set is separated by a new line in the text file. Help with code or logic is much appreciated!
so far I have the following. i am unsure of the logic moving forward when i reach a line break. My research here also suggests that i can use readarray -d.
#!/bin/bash
my_array=()
while IFS= read -r line || [[ "$line" ]]; do
if [[ $line -eq "" ]];
.
.
.
arr+=("$line") # i know this adds the value to the array
done < "$1"
printf '%s\n' "${my_array[#]}"
desired output:
array1 = (Paige Buckley6 Govan Mayer King)
array2 = (Harrison Atkins Reinhardt Wilson)
array3 = (Vaughan Sergovia Terrega)
#then loop through the each array one after the other.
Bash has no array-of-arrays. So you have to represent it in an other way.
You could leave the newlines and have an array of newline separated elements:
array=()
elem=""
while IFS= read -r line; do
if [[ "$line" != "" ]]; then
elem+="${elem:+$'\n'}$line" # accumulate lines in elem
else
array+=("$elem") # flush elem as array element
elem=""
fi
done
if [[ -n "$elem" ]]; then
array+=("$elem") # flush the last elem
fi
# iterate over array
for ((i=0;i<${#array[#]};++i)); do
# each array element is newline separated items
readarray -t elem <<<"${array[i]}"
printf 'array%d = (%s)\n' "$i" "${elem[*]}"
done
You could simplify the loop with some unique character and a sed for example like:
readarray -d '#' -t array < <(sed -z 's/\n\n/#/g' file)
But overall, this awk generates same output:
awk -v RS= -v FS='\n' '{
printf "array%d = (", NR;
for (i=1;i<=NF;++i) printf "%s%s", $i, i==NF?"":" ";
printf ")\n"
}'
Using nameref :
#!/usr/bin/env bash
declare -a array1 array2 array3
declare -n array=array$((n=1))
while IFS= read -r line; do
test "$line" = "" && declare -n array=array$((n=n+1)) || array+=("$line")
done < "$1"
declare -p array1 array2 array3
Called with :
bash test.sh data
# result
declare -a array1=([0]="Paige" [1]="Buckley" [2]="Govan" [3]="Mayer" [4]="King")
declare -a array2=([0]="Harrison" [1]="Atkins" [2]="Reinhardt" [3]="Wilson")
declare -a array3=([0]="Vaughan" [1]="Sergovia" [2]="Tarrega")
Assumptions:
blank links are truly blank (ie, no need to worry about any white space on said lines)
could have consecutive blank lines
names could have embedded white space
the number of groups could vary and won't always be 3 (as with the sample data provided in the question)
OP is open to using a (simulated) 2-dimensional array as opposed to a (variable) number of 1-dimensional arrays
My data file:
$ cat names.dat
<<< leading blank lines
Paige
Buckley
Govan
Mayer
King Kong
<<< consecutive blank lines
Harrison
Atkins
Reinhardt
Wilson
Larry
Moe
Curly
Shemp
Vaughan
Sergovia
Tarrega
<<< trailing blank lines
One idea that uses a couple arrays:
array #1: associative array - the previously mentioned (simulated) 2-dimensional array with the index - [x,y] - where x is a unique identifier for a group of names and y is a unique identifier for a name within a group
array #2: 1-dimensional array to keep track of max(y) for each group x
Loading the arrays:
unset names max_y # make sure array names are not already in use
declare -A names # declare associative array
x=1 # init group counter
y=0 # init name counter
max_y=() # initialize the max(y) array
inc= # clear increment flag
while read -r name
do
if [[ "${name}" = '' ]] # if we found a blank line ...
then
[[ "${y}" -eq 0 ]] && # if this is a leading blank line then ...
continue # ignore and skip to the next line
inc=y # set flag to increment 'x'
else
[[ "${inc}" = 'y' ]] && # if increment flag is set ...
max_y[${x}]="${y}" && # make note of max(y) for this 'x'
((x++)) && # increment 'x' (group counter)
y=0 && # reset 'y'
inc= # clear increment flag
((y++)) # increment 'y' (name counter)
names[${x},${y}]="${name}" # save the name
fi
done < names.dat
max_y[${x}]="${y}" # make note of the last max(y) value
Contents of the array:
$ typeset -p names
declare -A names=([1,5]="King Kong" [1,4]="Mayer" [1,1]="Paige" [1,3]="Govan" [1,2]="Buckley" [3,4]="Shemp" [3,3]="Curly" [3,2]="Moe" [3,1]="Larry" [2,4]="Wilson" [2,2]="Atkins" [2,3]="Reinhardt" [2,1]="Harrison" [4,1]="Vaughan" [4,2]="Sergovia" [4,3]="Tarrega" )
$ for (( i=1; i<=${x}; i++ ))
do
for (( j=1; j<=${max_y[${i}]}; j++ ))
do
echo "names[${i},${j}] : ${names[${i},${j}]}"
done
echo ""
done
names[1,1] : Paige
names[1,2] : Buckley
names[1,3] : Govan
names[1,4] : Mayer
names[1,5] : King Kong
names[2,1] : Harrison
names[2,2] : Atkins
names[2,3] : Reinhardt
names[2,4] : Wilson
names[3,1] : Larry
names[3,2] : Moe
names[3,3] : Curly
names[3,4] : Shemp
names[4,1] : Vaughan
names[4,2] : Sergovia
names[4,3] : Tarrega

Arrays in Bash Shell

I want to write a shell script to get the following output:
$ Enter String: a2b3
aabbb
I tried using for loops and arrays, but the loop count messes with the array index and leaves null elements within the array, making it impossible to print out the array as required.
The script used:
echo "Enter your alphanumeric string: "
read a
n=${#a}
for (( i=0;i<n;i++ ))
do
string[i]=${a:i:1}
if [[ ${string[i]} =~ [a-zA-Z] ]]
then
alpha[i]=${string[i]}
elif [[ ${string[i]} =~ [0-9] ]]
then
if [[ ${string[i+1]} =~ [0-9] ]]
then
num[i]=${string[i]}${string[i+1]}
elif ! [[ ${string[i+1]} =~ [0-9] ]]
then
num[i]=${string[i]}
fi
fi
done
n=${#num[*]}
for (( i=0;i<n;i++ ))
do
echo num[$i] = ${num[i]}
done
n=${#alpha[*]}
for (( i=0;i<n;i++ ))
do
echo alpha[$i] = ${alpha[i]}
done
The output I get for the same:
$ sh Q1.sh
Enter your alphanumeric string:
a6b3
num[0] =
num[1] = 6
alpha[0] = a
alpha[1] =
A better way to append an element to an array is with array+=(...). Then you don't have to worry about having the correct index on the left-hand side.
alpha+=("${string[i]}")
num+=("${string[i]}" "${string[i+1]}")

storing user values in an array then comparing these variables using bash

while read line
do
if [ $line -ge $zero ]
then
a+=($line) ##here I am attempting to store the values in an array
else
for i in ${a[#]}
do
echo $(a[$i]) ## here I am trying to print them
done
fi
what is going wrong? it is giving this error:
a[1]: command not found
a[2]: command not found
a[3]: command not found
done
From the begining
if [ $line -ge $zero ]
what data type should be in $line?
-ge used in numeric comparison. If $line is a string than use = or if you just wnt to check that it's not empty use this syntax if [[ "$line" ]]
Next.
a+=($line)
Again if $line is a string then you should wrap in "" like this a+=("$line")
coz line can contain spaces.
For loop.
for i in ${a[#]}
do
echo $(a[$i]) ## here I am trying to print them
You'r messing with syntax here for i in ${a[#]} will iterate over arrays values, not indexes. And again if values are strings use "" like this for i in "${a[#]}"
So this echo $(a[$i]) won't work by 2 reasons. First you should use {} here, like this echo ${a[$i]}, second $i is not index, but it's may actualy work if it's a digit but in a wrong way. So here you need just echo $i coz $i is alredy a value from a array. Or rewrite foor loop.
for i in ${!a[#]}
do
echo ${a[$i]} ## here I am trying to print them
done
And last but not least, there is no done at the end of this script or it's just a part? So in the and it should look like this.
while read line; do
if [[ $line ]]; then
a+=( "$line" ) ##here I am attempting to store the values in an array
else
for i in ${!a[#]}; do
echo ${a[$i]} ## here I am trying to print them
done
fi
done < data.txt
echo ${a[#]} # print rusult

Storing matching results from a regular expression in an array

I am trying to write a simple bash script that would grep for a string in a binary file and then using a regular expression extract all hexadecimal strings. However, I cannot seem to find a way in which I could store the output from the regex in an array which I could later on use to convert all of its elements from hexadecimal to ASCII values.
str=$(grep -ra "Html.Exploit.CVE.2015_6073" /var/lib);
hexstr=$([[ $STR =~ (?<=^|[*{};])[A-Fa-f0-9]+(?=$|[*;{}]) ]]);
converted=$(xxd -r -p <<< "$HEXSTR");
echo -e "\e[92m$converted \e[0m"
Can I do an if statement with a while loop in it that would create an array with the hexadecimal strings (returned from the regex) as elements?
UPDATE
Current code:
#!/bin/bash
str=$(grep -ra "Html.Exploit.CVE.2015_6073" /var/lib);
if [[ $str =~ (?<=^|[*{};])[A-Fa-f0-9]+(?=$|[*;{}]) ]]; then
Array+="$str"
fi
for hex in "${Array[#]}"; do
echo $hex
done
You can add values to an array like this in bash:
Array+=("$hexstr")
Then you can loop through the array like this:
for hex in "${Array[#]}"; do
#perform operation to convert hex here
converted="$(xxd -r -p <<< "$hex")"
done
To correct the if match logic you can make a statement like this so the $hexstr variable will get set.
if [[ $STR =~ (?<=^|[*{};])[A-Fa-f0-9]+(?=$|[*;{}]) ]]; then
hexstr="$STR"
fi
Putting this together would look like this:
if [[ $STR =~ (?<=^|[*{};])[A-Fa-f0-9]+(?=$|[*;{}]) ]]; then
Array+=("$STR")
fi
###Do work
###Use For loop etc
If you want to shorten your code even more (less code less chances to break) you can do all of the matching in the grep and add to the array like this:
Array=($( grep -ra "Html.Exploit.CVE.2015_6073" /var/lib | grep -oP "(?<=^|[*{};])[A-Fa-f0-9]+(?=$|[*;{}])"))
##Do For loop
The above will perform your grep and find only matching lines then it will get only the matching value of the regex and store each in the array.

Resources