8

假设我有以下 2 个数组。数组始终按字母顺序排序。

arr1=(a b c)
arr2=(a b c d)

我必须比较这两个数组,如果它们完全相同,则必须返回 true。

4

4 回答 4

12

我为您找到了一个可能的解决方案,使用 STDIN 和 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

编辑:一点解释:

它从您的数组中生成一个字符串,并通过 STDIN 将它们提供给 diff。diff要么返回差异,要么什么都不返回,我将其填充到一个变量中,然后用 -z 测试内容。

于 2013-08-01T08:51:23.887 回答
11

将所有位置参数作为"$*"一个单词获取,这可以使它:

[ "${arr1[*]}" == "${arr2[*]}" ] && echo "equal" || echo "distinct"

请注意,表达式[ condition ] && echo "equal" || echo "distinct"等价于

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

当元素没有空格时,它只能 100% 工作(参见评论中的讨论)。

于 2013-08-01T08:40:17.983 回答
4

我能想到的最稳健的方法是:

  • 首先比较数组大小
  • 逐个元素比较

所以:

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

使用的结构是:

  • ${#array[*]}它返回数组中元素的数量
  • ${!array[*]}返回索引列表(而不是${array[*]}返回元素)

上面应该处理数组值、稀疏数组和关联数组中的空白(尽管它并不总是捕获关联数组中的不同索引,但您需要对此进行额外的测试)。

于 2013-08-01T09:48:33.470 回答
2

对于那些想要使用函数而不是直接检查的解决方案的人,这是我编写的解决方案。后者适用于普通数组、关联数组和稀疏数组。但它至少需要 Bash 4.3。

这个函数是我写的库的一部分。作为我的库中的约定,我使用返回值和retval全局参数来表示比简单数字更复杂的返回语句。这同样适用于errorWithLog,如果后者可用,它只是一个回stderr显并记录为系统记录器的错误。

#-------------------------------------------------------------------------------
# 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
}

使用示例:


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

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

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

no.


table1=(1 2 3 4)
table2=(1 2 "3 4")

if isArraysEqual table1 table2; then
    echo "yes"
else
    echo "no"
fi

no.

于 2016-04-24T14:53:28.423 回答