Вопрос или проблема
У меня есть сценарий, в котором я хочу сравнить значения 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 “успех” или “НЕУДАЧА”
Успех – когда все данные в array1 совпадают с array2 [индекс к индексу]
Неудача – когда значение на индексе не совпадает, тогда неудача
Вы можете просто сравнить массивы как строки:
if [[ "${array1[*]}" == "${array2[*]}" ]]; then
echo успех
else
echo НЕУДАЧА
fi
Обратите внимание на использование [*]
, а не [@]
— первое объединяет элементы в одну строку (используя первый символ $IFS в качестве соединителя).
Сравнение как строки может привести к ложным срабатываниям, если некоторые элементы содержат символ соединителя. Например
array1=( "foo bar" baz ) # 2 элемента
array2=( foo bar baz ) # 3 элемента
[[ "${array1[*]}" == "${array2[*]}" ]] && echo равны
# => равны
Более тщательный тест на равенство
- проверяет, что массивы имеют одинаковую длину, и
- проверяет, что каждый элемент равен:
Эта функция использует namerefs, доступные в (я думаю) 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 успех
else
echo НЕУДАЧА
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 истинно
fi
В случае bash
< 4.4 используйте:
[ "`printf '%s\n' ${a[@]+"${a[@]}"} | sort -t$'\n'`" = "`printf '%s\n' ${b[@]+"${b[@]}"} | sort -t'\n'`" ]
Вы не упоминаете, какую оболочку вы используете, но синтаксис выглядит так, будто это оболочка, подобная 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'
Где ключи и цитированные значения чередуются и разделяются пробелами.
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. Данный код проверит, сопоставляются ли значения по индексам и выведет "success", если все значения совпадают, или "FAIL", если хотя бы одно значение не совпадает.
Пример кода:
# Объявляем массивы
array1=(12.12 10.1)
array2=(12.12 10.1)
# Функция для сравнения массивов
arrays_equal() {
local -n _a1=$1 _a2=$2
local i
# Проверяем длину массивов
(( ${#_a1[@]} == ${#_a2[@]} )) || return 1
# Сравниваем элементы по индексам
for ((i = 0; i < ${#_a1[@]}; i++)); do
[[ "${_a1[i]}" == "${_a2[i]}" ]] || return 1
done
return 0
}
# Вызов функции для проверки равенства массивов
if arrays_equal array1 array2; then
echo success
else
echo FAIL
fi
Объяснение кода:
-
Объявление массивов: Мы создали два массива
array1
иarray2
с одинаковыми значениями. -
Функция
arrays_equal
:- Использует
nameref
для создания ссылок на входящие массивы. - Сравнивает длину массивов. Если длины не совпадают, функция завершает выполнение с кодом 1.
- Выполняет поэлементное сравнение каждого значения в массиве. Если найдется хотя бы одно не совпадающее значение, функция также завершает выполнение с кодом 1.
- Использует
-
Проверка результатов: После проверки массивов с помощью функции, в зависимости от результата выводится "success" или "FAIL".
Примечания:
- Убедитесь, что вы используете Bash версии 4.4 или выше, чтобы воспользоваться возможностями
nameref
. - Если один из массивов будет пустым, проверка длины также гарантирует, что вы получите правильный вывод.
Этот метод позволяет эффективно и безопасно сравнивать значения массивов индекс за индексом, что минимизирует вероятность ошибок, связанных с неправильным сравнением.