Як перевірити, чи всі елементи масиву рівні в баші?


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

Я просто чекаю найкращої відповіді, не хвилюйтесь незабаром, я проголосую
yael

Я мав на увазі всіх інших. Це питання заслуговує на підсумок ІМО.
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". Інакше друкуйте "погано".

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

На відміну від моєї попередньої версії, я не можу wcтут використовуватись, оскільки для цього потрібні рядки вводу, що закінчуються новим рядком ... і використовувати sedабо trперетворювати sortNUL в нові рядки після того, як буде перервано мета використання роздільників 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

Привіт Кас, я віддаю перевагу вашій попередній відповіді
яель

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на початку функції, якщо цього не хочете.


всі ці відповіді, здається, не підтримують баш?
yael

@yael, див. редагування для баш-рішення. Але для чого ти б використовував 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

У мене є ще одне баш-рішення, яке також повинно працювати з рядками:

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

Баш єдине рішення (припускаючи , що 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 і дякуємо за ваш внесок! Цей код надрукує "Матч", навіть якщо буде виявлено невідповідність ... це призначено?
фра-сан
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.