Arrays in Bash Shell - arrays

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]}")

Related

Cat strings from file and compare it with associative bash array

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.

Bash - How to check if value doesn't exist in array?

Folks,
I have an array, ex,
declare -a arr=("crm" "hr" "pos")
I need to output error if the passed value doesn't exist in this array
I'm trying this use below snippet but it prints "No match found" for any value
match=0
for i in "${arr[#]}"; do
if ! [[ $2 == "$i" ]]; then
match=1
break
fi
done
if [[ $match = 1 ]]; then
echo "No match found"
fi
Any idea how to loop in array and popup error if value doesn't exist ?
There are already answers about your question see check value is in an array, but a fix/idea for your specific approach is something like.
#!/usr/bin/env bash
declare -a arr=("crm" "hr" "pos")
match=0
for i in "${arr[#]}"; do
if ! [[ $2 == "$i" ]]; then
((match++))
fi
done
if (( match == ${#arr[*]} )); then
printf >&2 "No match found\n"
fi
The above script increments match every time the test inside the for loop is true, and in the end compare match with the length of the array ${#arr[*]}. A more verbose output is to put set -x on after the shebang and add a $ sign to the variable match (which is not required) inside the (( )).
if (( $match == ${#arr[*]} )); then
Your original approach always breaks the loop when it does not have a match, doing so will not continue the loop.
You reversed the logic. Just leave out the ! and swap the final test:
match=0
for i in "${arr[#]}"; do
if [[ $2 == "$i" ]]; then
match=1
break
fi
done
if [[ $match = 0 ]]; then
echo "No match found"
fi
This doesn't require a loop.
c.f. if a 2 strings BOTH present in 1 array in a bash script
$: x=bar
$: [[ " ${arr[*]} " =~ " $x " ]] && echo found || echo nope
nope
$: x=hr
$: [[ " ${arr[*]} " =~ " $x " ]] && echo found || echo nope
found
or
$: match=0
$: set -- foo bar
$: [[ " ${arr[*]} " =~ " $2 " ]] && match=1
$: echo match
0
$: set -- foo crm
$: [[ " ${arr[*]} " =~ " $2 " ]] && match=1
$: echo match
1
The logic seems incorrect.
If it does not match, you break the loop.
If it does match, the loop goes to the next iteration. Then it becomes unmatched and finally breaks the loop
declare -a arr=("crm" "hr" "pos")
not_match=0
for i in "${arr[#]}"; do
if [ "$2" = "$i" ]; then
not_match=1
break
fi
done
if [ $not_match -eq 0 ]; then
echo "No match found"
fi

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

bash - pulling content from array via index

I am having trouble with a script that prints out the index and items of the array and then allows the user to input an index that will print out that specific item of the array.
array=(abc def ghi)
i=0
while [ $i -lt ${#array[*]} ]; do
echo "[$i] ${array[$i]}"
i=$(($i+1));
done
echo -e "select an index: "; read answer
#this is the part that is troubling me
for index in ${!array[*]}; do
if [[ $answer == $index ]]; then
echo ${array[$index]}
break
else
echo "invalid"
break
fi
done
so if the user enters 0, it should print abc. 1 would be def etc. It currently only works for index 0.
Couple of problems in there that I can find:
you need a space right after $item to separate from the ]
$item should probably be $index
most importantly, remove the break after the "echo invalid" which is the reason the loop exits right after testing against '0' and doesn't check for the next array indexes. Using a flag then you should control if the answer matched any of the valid indexes, otherwise print the "invalid" message. I used a very explicit string test there for clarity.
A running code should look like:
array=(abc def ghi)
i=0
while [ $i -lt ${#array[*]} ]; do
echo "[$i] ${array[$i]}"
i=$(($i+1));
done
echo -e "select an index: "; read answer
for index in ${!array[*]}; do
found="FALSE"
if [[ "$answer" == "$index" ]]; then
found="TRUE"
echo "${array[$index]}"
break
fi
done
if [ "$found" == "FALSE" ]; then
echo "Invalid input $answer"
fi

Check if an element is present in a Bash array [duplicate]

This question already has answers here:
Check if a Bash array contains a value
(41 answers)
Closed 2 years ago.
I was wondering if there is an efficient way to check if an element is present within an array in Bash? I am looking for something similar to what I can do in Python, like:
arr = ['a','b','c','d']
if 'd' in arr:
do your thing
else:
do something
I've seen solutions using associative array for bash for Bash 4+, but I am wondering if there is another solution out there.
Please understand that I know the trivial solution is to iterate in the array, but I don't want that.
You could do:
if [[ " ${arr[*]} " == *" d "* ]]; then
echo "arr contains d"
fi
This will give false positives for example if you look for "a b" -- that substring is in the joined string but not as an array element. This dilemma will occur for whatever delimiter you choose.
The safest way is to loop over the array until you find the element:
array_contains () {
local seeking=$1; shift
local in=1
for element; do
if [[ $element == "$seeking" ]]; then
in=0
break
fi
done
return $in
}
arr=(a b c "d e" f g)
array_contains "a b" "${arr[#]}" && echo yes || echo no # no
array_contains "d e" "${arr[#]}" && echo yes || echo no # yes
Here's a "cleaner" version where you just pass the array name, not all its elements
array_contains2 () {
local array="$1[#]"
local seeking=$2
local in=1
for element in "${!array}"; do
if [[ $element == "$seeking" ]]; then
in=0
break
fi
done
return $in
}
array_contains2 arr "a b" && echo yes || echo no # no
array_contains2 arr "d e" && echo yes || echo no # yes
For associative arrays, there's a very tidy way to test if the array contains a given key: The -v operator
$ declare -A arr=( [foo]=bar [baz]=qux )
$ [[ -v arr[foo] ]] && echo yes || echo no
yes
$ [[ -v arr[bar] ]] && echo yes || echo no
no
See 6.4 Bash Conditional Expressions in the manual.
Obvious caveats aside, if your array was actually like the one above, you could do
if [[ ${arr[*]} =~ d ]]
then
do your thing
else
do something
fi
Initialize array arr and add elements
set variable to search for SEARCH_STRING
check if your array contains element
arr=()
arr+=('a')
arr+=('b')
arr+=('c')
SEARCH_STRING='b'
if [[ " ${arr[*]} " == *"$SEARCH_STRING"* ]];
then
echo "YES, your arr contains $SEARCH_STRING"
else
echo "NO, your arr does not contain $SEARCH_STRING"
fi
If array elements don't contain spaces, another (perhaps more readable) solution would be:
if echo ${arr[#]} | grep -q -w "d"; then
echo "is in array"
else
echo "is not in array"
fi
array=("word" "two words") # let's look for "two words"
using grep and printf:
(printf '%s\n' "${array[#]}" | grep -x -q "two words") && <run_your_if_found_command_here>
using for:
(for e in "${array[#]}"; do [[ "$e" == "two words" ]] && exit 0; done; exit 1) && <run_your_if_found_command_here>
For not_found results add || <run_your_if_notfound_command_here>
As bash does not have a built-in value in array operator and the =~ operator or the [[ "${array[#]" == *"${item}"* ]] notation keep confusing me, I usually combine grep with a here-string:
colors=('black' 'blue' 'light green')
if grep -q 'black' <<< "${colors[#]}"
then
echo 'match'
fi
Beware however that this suffers from the same false positives issue as many of the other answers that occurs when the item to search for is fully contained, but is not equal to another item:
if grep -q 'green' <<< "${colors[#]}"
then
echo 'should not match, but does'
fi
If that is an issue for your use case, you probably won't get around looping over the array:
for color in "${colors[#]}"
do
if [ "${color}" = 'green' ]
then
echo "should not match and won't"
break
fi
done
for color in "${colors[#]}"
do
if [ "${color}" = 'light green' ]
then
echo 'match'
break
fi
done
Here's another way that might be faster, in terms of compute time, than iterating. Not sure. The idea is to convert the array to a string, truncate it, and get the size of the new array.
For example, to find the index of 'd':
arr=(a b c d)
temp=`echo ${arr[#]}`
temp=( ${temp%%d*} )
index=${#temp[#]}
You could turn this into a function like:
get-index() {
Item=$1
Array="$2[#]"
ArgArray=( ${!Array} )
NewArray=( ${!Array%%${Item}*} )
Index=${#NewArray[#]}
[[ ${#ArgArray[#]} == ${#NewArray[#]} ]] && echo -1 || echo $Index
}
You could then call:
get-index d arr
and it would echo back 3, which would be assignable with:
index=`get-index d arr`
FWIW, here's what I used:
expr "${arr[*]}" : ".*\<$item\>"
This works where there are no delimiters in any of the array items or in the search target. I didn't need to solve the general case for my applicaiton.

Resources