сравнение значений двух массивов на равенство | индекс к индексу |

Вопрос или проблема

У меня есть задача, в которой я хочу сравнить значения массива array1 и array2.

echo "${array1[@]}"

Выводит output1: 12.12 10.1

echo "${array2[@]}"

Выводит output2: 12.12 10.1

Как сравнить значения array1 с array2 следующим образом:

array1[0]=array2[0]
array1[1]=array2[1]

12.12 12.12
10.1 10.1

Если совпадают, тогда вывести:

echo “success” или “FAIL”

Успех – когда все данные в array1 совпадают с array2 [индекс к индексу]

Ошибка – когда значение индекс к индексу не совпадает, тогда ошибка

Вы можете просто сравнить массивы как строки:

if [[ "${array1[*]}" == "${array2[*]}" ]]; then
  echo success
else
  echo FAIL
fi

Обратите внимание на использование [*], а не [@] — первое объединяет элементы в одну строку (используя первый символ $IFS в качестве разделителя).


Сравнение как строки может привести к ложным срабатываниям, если некоторые элементы содержат символ-разделитель. Например:

array1=( "foo bar"  baz )   # 2 элемента
array2=( foo  bar  baz )    # 3 элемента

[[ "${array1[*]}" == "${array2[*]}" ]] && echo equal
# => equal

Более комплексный тест на равенство

  1. проверяет, что массивы одной длины, и
  2. проверяет, что каждый элемент равен:

Эта функция использует именованные ссылки, доступные в (по-моему) bash 4.4 и выше.

arrays_equal() {
    local -n _a1=$1 _a2=$2
    local i    

    # тест длины массива
    (( ${#_a1[@]} == ${#_a2[@]} )) || return 1

    # сравнение элементов
    for ((i = ${#_a1[@]} - 1; i >= 0; i--)); do
        [[ "${_a1[i]}" == "${_a2[i]}" ]] || return 1
    done
    return 0
}

if arrays_equal array1 array2; then
  echo success
else
  echo FAIL
fi

В случае, если элементы не могут содержать \n и вы хотите выполнить сравнением, не чувствительным к порядку:

arrays_equal() {
    [ "$1" = a ] || local -n a=$1
    [ "$2" = b ] || local -n b=$2
    (( ${#a[@]} == ${#b[@]} )) || return 1
    [ "`printf '%s\n' "${a[@]}" | sort -t$'\n'`" = "`printf '%s\n' "${b[@]}" | sort -t$'\n'`" ]
}
a=(b c)
b=(c b)
if arrays_equal a b; then
    echo true
fi

В случае bash < 4.4 используйте:

[ "`printf '%s\n' ${a[@]+"${a[@]}"} | sort -t$'\n'`" = "`printf '%s\n' ${b[@]+"${b[@]}"} | sort -t'\n'`" ]

Вы не упоминаете, какой shell используете, но синтаксис выглядит так, будто это оболочка, похожая на Korn, скорее всего, bash, где, как и в ksh, индексы массивов начинаются с 0, а массивы разреженные, поэтому больше похожи на ассоциативные массивы с ключами, ограниченными положительными целыми числами.

В bash версии 5.1 или новее вы можете сравнить вывод "${array[@]@K}", который выглядит так:

$ bash -c $'a[5]=a a[1]= a[123]="\n"; printf "%s\n" "${a[@]@K}"'
1 "" 5 "a" 123 $'\n'

Где ключи и quotированные значения чередуются и разделяются пробелами.

if [[ "${a[@]@K}" = "${b[@]@K}" ]]; then
  echo a и b имеют одинаковый список ключей и значений
fi

В более старых версиях вы могли сравнить вывод declare -p после замены имени переменной на временное значение или удаления его, или если вы можете гарантировать, что переменные определены и являются массивами, удалить все до =:

$ bash -c $'a[5]=a a[1]= a[123]="\n"; declare -p a'
declare -a a=([1]="" [5]="a" [123]=$'\n')
a_declare=$(declare -p a)
b_declare=$(declare -p b)

shopt -s extglob
if [[ "${a_declare/ +([^-=])}" = "${b_declare/ +([^-=])}" ]]; then
  echo Переменные идентичны по атрибутам, ключам и значениям
fi

Если есть символ, который вы знаете, что не встречается в элементах массива, вы также можете использовать строковое сравнение по спискам ключей и значений после их объединения с этим символом:

IFS='|'
if [[ "${!a[*]}" = "${!b[*]}" && "${a[*]}" = "${b[*]}" ]]; then
  echo a и b имеют одинаковый список ключей и значений
fi

Для информации, в zsh, которая не может быть оболочкой, которую вы используете, так как индексы массива начинаются с 1, а не с 0, и где массивы являются обычными массивами, вы можете просто сравнить развертывания ${(q)array}, которые развертываются в элементы, каждый из которых заключен в кавычки (и объединен с первым символом $IFS, когда не в контексте списка, как обычно, хотя вы можете явно объединить с произвольным символом, как показано в ответе на Linux инструменты для обработки файлов как множеств и выполнения операций над множествами).

if [[ ${(q)a} = ${(q)b} ]]; then
  echo a и b имеют одинаковый список элементов в одном и том же порядке.
fi

Ответ или решение

Для сравнения значений двух массивов array1 и array2 по индексу, чтобы определить, равны ли они друг другу, мы можем использовать множество подходов в bash. Вот пошаговое решение, которое демонстрирует, как это сделать.

Пример массивов

Рассмотрим ваш пример, где у нас есть два массива:

array1=( "12.12" "10.1" )
array2=( "12.12" "10.1" )

Создание функции для сравнения массивов

Мы создадим функцию arrays_equal, которая будет сравнивать массивы по индексу. Эта функция будет учитывать длину массивов и поэлементное сравнение.

arrays_equal() {
    local -n a1=$1
    local -n a2=$2

    # Проверяем длину массивов
    if [[ ${#a1[@]} -ne ${#a2[@]} ]]; then
        return 1
    fi

    # Сравниваем элементы
    for ((i = 0; i < ${#a1[@]}; i++)); do
        if [[ "${a1[i]}" != "${a2[i]}" ]]; then
            return 1
        fi
    done

    return 0
}

Вызов функции и вывод результата

Теперь, когда у нас есть функция сравнения, давайте её вызовем и выведем результат:

if arrays_equal array1 array2; then
    echo "success"
else
    echo "FAIL"
fi

Полный скрипт

Объединим всё вместе в один скрипт:

#!/bin/bash

array1=( "12.12" "10.1" )
array2=( "12.12" "10.1" )

arrays_equal() {
    local -n a1=$1
    local -n a2=$2

    # Проверяем длину массивов
    if [[ ${#a1[@]} -ne ${#a2[@]} ]]; then
        return 1
    fi

    # Сравниваем элементы
    for ((i = 0; i < ${#a1[@]}; i++)); do
        if [[ "${a1[i]}" != "${a2[i]}" ]]; then
            return 1
        fi
    done

    return 0
}

if arrays_equal array1 array2; then
    echo "success"
else
    echo "FAIL"
fi

Заключение

Таким образом, представленное решение позволяет корректно сравнивать два массива по индексу и выдавать результат в зависимости от того, совпадают ли их значения. Если все элементы совпадают, выводится "success", в противном случае — "FAIL".

Не забудьте, что функция arrays_equal использует nameref, поэтому для её работы необходим Bash версии 4.3 или выше.

Оцените материал
Добавить комментарий

Капча загружается...