Вопрос или проблема
У меня есть задача, в которой я хочу сравнить значения массива 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
Более комплексный тест на равенство
- проверяет, что массивы одной длины, и
- проверяет, что каждый элемент равен:
Эта функция использует именованные ссылки, доступные в (по-моему) 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 или выше.