Как проверить, все ли элементы массива равны в bash?


15

Следующий массив представляет количество дисков на каждой машине Linux

Каждый отдельный массив содержит количество дисков на компьютере с Linux .

echo ${ARRAY_DISK_Quantity[*]}
4 4 4 4 2 4 4 4

Какой простой способ определить, что все значения массива равны?

Хороший статус:

4 4 4 4 4 4 4 4

Плохой статус:

4 4 4 4 4 4 2 4

Плохой статус:

6 6 6 6 6 6 6 6 6 6 2 6 2

Так много ответов и нет голосов?
Jesse_b

Это будет только тестирование целых чисел или тестирование строк?
Jesse_b

Я просто жду лучшего ответа, не волнуйтесь, скоро я буду голосовать
яэль

Я имел в виду всех остальных. Этот вопрос заслуживает одобрения ИМО.
Jesse_b

как только вам понадобится что-то, по крайней мере, такого уровня сложности, самое время начать использовать настоящий язык программирования, пока не
Отображаемое имя

Ответы:


11

bash+ GNU sort+ GNU grepрешение:

if [ "${#array[@]}" -gt 0 ] && [ $(printf "%s\000" "${array[@]}" | 
       LC_ALL=C sort -z -u |
       grep -z -c .) -eq 1 ] ; then
  echo ok
else
  echo bad
fi

Английское объяснение: если уникальная сортировка элементов массива приводит только к одному элементу, выведите «ok». В противном случае выведите «bad».

Массив печатается с байтами NUL, отделяющими каждый элемент, передается в сортировку GNU (полагаясь на параметры -zaka --zero-terminatedи -uaka --unique), а затем в grep(используя параметры -zaka --null-dataи -caka --count) для подсчета выходных строк.

В отличие от моей предыдущей версии, я не могу использовать wcздесь, потому что для этого требуются строки ввода, заканчивающиеся символом новой строки ... и использующие sedили trдля преобразования NUL в новые строки после того, как sortони потерпят поражение от использования разделителей NUL. grep -cделает разумную замену.


Вот то же самое, переписанное как функция:

function count_unique() {
  local LC_ALL=C

  if [ "$#" -eq 0 ] ; then 
    echo 0
  else
    echo "$(printf "%s\000" "$@" |
              sort --zero-terminated --unique |
              grep --null-data --count .)"
  fi
}



ARRAY_DISK_Quantity=(4 4 4 4 2 4 4 4)

if [ "$(count_unique "${ARRAY_DISK_Quantity[@]}")" -eq 1 ] ; then
  echo "ok"
else
  echo "bad"
fi

1
Обратите внимание, что sort -uне возвращает уникальные элементы, но один из каждого набора элементов, которые сортируются одинаково. Например, было бы сказано «хорошо» ARRAY_DISK_Quantity=(① ②)в системах GNU, где локали обычно решают, что эти 2 символа сортируются одинаково. Вы хотели бы LC_ALL=C sort -uдля байтовой уникальности.
Стефан

просто еще одно замечание, что это будет сбой также в том случае, если из CLI не появятся дополнительные диски, поэтому нужно также добавить этот синтаксис
yael

[[`printf"% s \ n "" $ {ARRAY_DISK_Quantity [@]} "| wc -l `-eq` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | grep -c "0" `]] && echo fail
yael

@ StéphaneChazelas, проблема локали, с которой стоит столкнуться, так же как и проблема IFS. Тестирование на пустой список, IMO, лучше всего делать отдельно - нет необходимости проверять неуникальные элементы в пустом наборе.
Cas

Привет Cas Я предпочитаю ваш предыдущий ответ
яэль

8

С zsh:

if ((${#${(u)ARRAY_DISK_Quantity[@]}} == 1)); then
  echo OK
else
  echo not OK
fi

Где (u)флаг расширения параметра для расширения уникальных значений. Таким образом, мы получаем количество уникальных значений в массиве.

Замените == 1на <= 1, если вы хотите считать пустой массив в порядке.

С помощью ksh93вы можете отсортировать массив и проверить, что первый элемент совпадает с последним:

set -s -- "${ARRAY_DISK_Quantity[@]}"
if [ "$1" = "${@: -1}" ]; then
  echo OK
else
  echo not OK
fi

С помощью ksh88 или pdksh / mksh:

set -s -- "${ARRAY_DISK_Quantity[@]}"
if eval '[ "$1" = "${'"$#"'}" ]'; then
  echo OK
else
  echo not OK
fi

С bash, вероятно, вам понадобится цикл:

unique_values() {
  typeset i
  for i do
    [ "$1" = "$i" ] || return 1
  done
  return 0
}
if unique_values "${ARRAY_DISK_Quantity[@]}"; then
  echo OK
else
  echo not OK
fi

(будет работать со всеми подобными Борну оболочками с поддержкой массивов (ksh, zsh, bash, yash)).

Обратите внимание, что он возвращает ОК для пустого массива. Добавьте [ "$#" -gt 0 ] || returnв начале функции, если вы этого не хотите.


все эти ответы, кажется, не поддерживают Bash?
Яэль

@yael, см. edit для решения bash. Но зачем вы используете bash?
Стефан

В Bash на странице справки typesetуказано Obsolete. See `help declare'., почему вы используете ее вместо localили declare?
wjandrea

1
@wjandrea typesetработает во всех 4 оболочках. Он также был оригинальным от ksh в начале 80-х (bash в основном копировал ksh88, когда дело доходит до установки и объявления типа переменной области видимости, но решил переименовать typeset declareи сделать typesetпсевдоним для объявления).
Стефан

4

bash+ awkрешение:

function get_status() {
    arr=("$@")    # get the array passed as argument
    if awk 'v && $1!=v{ exit 1 }{ v=$1 }' <(printf "%d\n" "${arr[@]}"); then 
        echo "status: Ok"
    else 
        echo "status: Bad"
    fi
}

Тестовый пример № 1:

ARRAY_DISK_Quantity=(4 4 4 4 4 2 4 4)
get_status "${ARRAY_DISK_Quantity[@]}"
status: Bad

Тестовый пример № 2:

ARRAY_DISK_Quantity=(4 4 4 4 4 4 4 4)
get_status "${ARRAY_DISK_Quantity[@]}"
status: Ok

4

У меня есть другое решение только для bash, которое должно работать и со строками:

isarray.equal () {
    local placeholder="$1"
    local num=0
    while (( $# )); do
        if [[ "$1" != "$placeholder" ]]; then
            num=1
            echo 'Bad' && break
        fi
        shift
    done
    [[ "$num" -ne 1 ]] && echo 'Okay'
}

Демонстрация:

[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(4 4 4 4 2 4 4 4)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Bad
[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(4 4 4 4 4 4 4 4)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Okay
[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(four four four four two four four four)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Bad
[root@JBSTEST001 ~]# ARRAY_DISK_Quantity=(four four four four four four four four)
[root@JBSTEST001 ~]# isarray.equal "${ARRAY_DISK_Quantity[@]}"
Okay

Обратите внимание, что точки недопустимы в именах функций, хотя Bash довольно допустим. Это может вызвать проблемы, как при экспорте функции.
wjandrea


2

С bash и GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi

Да, но как насчет (10 10 10 10)? В остальном довольно мило.
Джо

@Joe: хороший улов. Я обновил свой ответ.
Сайрус


0

решение только для bash (если aесть ARRAY_DISK_Quantity)

ttt=${a[0]}
res=0
for i in "${a[@]}"
do 
    let res+=$(if [ "$ttt" -ne "$i" ]; then echo 1; else echo 0; fi);  
done
if [ "$res" -eq 0 ]
then 
    echo "ok"
else
    echo "bad"
fi

Работает, но подсчитывает все ошибки, когда достаточно одной:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Джо

0

Используйте цикл for для сравнения каждого элемента массива со следующим. Завершите цикл на одну итерацию меньше длины массива, чтобы избежать сравнения последнего элемента ни с чем в конце.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"

Добро пожаловать на U & L и спасибо за ваш вклад! Этот код выведет «Match», даже если обнаружено несоответствие ... это предназначено?
Фра-Сан
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.