Let's say I have the following 2 arrays. The arrays are always sorted alphabetically.
arr1=(a b c)
arr2=(a b c d)
I have to compare these two arrays and if they are exactly the same then true must be returned.
I got a possible solution for you, using STDIN and diff:
#!/bin/bash
arr1=(a b c)
arr2=(a b c d)
diff=$(diff <(printf "%s\n" "${arr1[@]}") <(printf "%s\n" "${arr2[@]}"))
if [[ -z "$diff" ]]; then
echo "TRUE"
else
echo "FALSE"
fi
EDIT: A little explanation:
It makes a string out of your arrays and feeds them both to diff via STDIN. diff
either returns the difference or nothing, which i stuff into a variable and test with -z for content later.
As "$*"
gets all the positional parameters as a single word, this can make it:
[ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"
Note that the expression [ condition ] && echo "equal" || echo "distinct"
is equivalent to
if [ condition ]; then
echo "equal"
else
echo "distinct"
fi
$ arr1=(a b c)
$ arr2=(a b c d)
$ [ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"
distinct
$ arr2=(a b c)
$ [ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"
equal
It just works 100% when having elements without spaces (see discussion in comments).
The most robust way I can think of is:
So:
arr1=(1 2 3 )
arr2=(1 2 "3 4")
[ ${#arr1[*]} != ${#arr2[*]} ] && { echo arrays different size; exit 1; }
for ii in ${!arr1[*]}; do
[ "${arr1[$ii]}" == "${arr2[$ii]}" ] || { echo different element $ii; exit 1; }
done
echo arrays identical
exit 0
Constructions used are:
${#array[*]}
which returns the number of elements in an array${!array[*]}
which returns a list of indexes (rather than ${array[*]}
which returns the elements)The above should handle whitespace within array values, sparse arrays, and associative arrays (though it does not always catch different indexes in associative arrays, you'd need an extra test for that).
For those of you who want a solution using a function instead of checking directly, here is a solution I wrote. The latter works with normal arrays, associative arrays and sparse arrays. But it requires at least Bash 4.3.
This function is part of the lib I wrote. As a convention in my lib, I'm using return values and a retval
global argument for return statements more complex than a simple number. The same applies for errorWithLog
, which is just an echo on stderr
and logged as an error to the system logger if the latter is available.
#-------------------------------------------------------------------------------
# I: - The array to compare tableA[@]
# - The second array tableB[@] against which to compare the first array
# P: Search if both arrays (all types) are equal
# NOTE: The tables must be passed *AS NAME* as myTable not as $myTable[@]
# nor ${myTable[@]} and requires Bash 4.3
# Inspired from http://stackoverflow.com/a/17990637/3514658
# and from http://stackoverflow.com/a/4017175/3514658
# O: - If both arrays are equal:
# - retval: true
# - return value for direct usage in a if statement: 0
# - If both arrays are not equal:
# - retval: false
# - return value for direct usage in a if statement: 1
#-------------------------------------------------------------------------------
function isArraysEqual() {
# Accessing by dereference using -n is new in Bash 4.3.
local -n arr1=$1 2>/dev/null
local -n arr2=$2 2>/dev/null
# If <Bash 4.3, need to use the following syntax, but checking the keys of
# associative arrays is not supported and, in that case, tables must be
# passed as names *WITH* elements i.e.: myTable[@]
# local -a arr1=("${!1}")
# local -a arr2=("${!2}")
if [ $? -ne 0 ]; then
errorWithLog "isArraysEqual() accessing using dereference with -n"\
"needs at least Bash 4.3. Arrays reported as different."
retval=false
return 1
fi
# Check size first. This is way faster than checking each item over
# iteration.
if [ ${#arr1[@]} != ${#arr2[@]} ]; then
retval=false
return 1
fi
# The ! expands to a list of array keys. For normal arrays, not associative
# arrays, this gives a list of index values starting from 0.
local -a arr1Keys=("${!arr1[@]}")
local -a arr2Keys=("${!arr2[@]}")
for (( i = 0; i < ${#arr1[@]}; i += 1 )); do
key=${arr1Keys[$i]}
# Check if the values are the same. If the key does not exist in arr2
# and the key does exist but is null in arr1, the values are NOT
# considered different. This is why checking keys is mandatory.
if [ "${arr1[$key]}" != "${arr2[$key]}" ]; then
retval=false
return 1
fi
# Check if keys are the same. This is needed for associative arrays.
if [ "${arr1Keys[$i]}" != "${arr2Keys[$i]}" ]; then
retval=false
return 1
fi
done
retval=true
return 0
}
Usage examples:
declare -A table1=([hello]=world [ab]=cd)
declare -A table2=([hello]=world [ab]=cd)
if isArraysEqual table1[@] table2[@]; then
echo "yes"
else
echo "no"
fi
Gives yes
.
declare -A table1=([hello]=world [ab]=cd)
declare -A table2=([hello]=world [ab]=cde)
if isArraysEqual table1 table2; then
echo "yes"
else
echo "no"
fi
Gives no
.
declare -A table1=([hello]=world [abhe]=ce)
declare -A table2=([hello]=world [ab he]=ce)
if isArraysEqual table1 table2; then
echo "yes"
else
echo "no"
fi
Gives no
.
table1=(1 2 3 4)
table2=(1 2 "3 4")
if isArraysEqual table1 table2; then
echo "yes"
else
echo "no"
fi
Gives no
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With