Como testar se os elementos da matriz são todos iguais no bash?

15

A seguinte matriz representou o número de discos em cada máquina Linux

Cada matriz única inclui o número de discos em uma máquina Linux .

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

qual é a maneira simples de identificar se todos os valores da matriz são iguais?

Bom status:

4 4 4 4 4 4 4 4

Status ruim:

4 4 4 4 4 4 2 4

Status ruim:

6 6 6 6 6 6 6 6 6 6 2 6 2
yael
fonte
Tantas respostas e sem votos?
Jesse_b
Isso testará apenas números inteiros ou também deve testar cadeias de caracteres?
Jesse_b
Eu apenas esperando o melhor preocupação não faça resposta em breve vou votar
yael
Eu quis dizer todo mundo. Esta questão merece aprovação da OMI.
Jesse_b
Depois que você precisar de algo com pelo menos esse nível de complexidade, é um bom momento para começar a usar uma linguagem de programação real, até que seja tarde demais ...
Nome para exibição

Respostas:

11

bashSolução + 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

Explicação em inglês: se a classificação exclusiva dos elementos da matriz resultar em apenas um elemento, imprima "ok". Caso contrário, imprima "incorreto".

A matriz é impresso com NUL bytes separando cada elemento, canalizada para GNU tipo (baseando-se no -zaka --zero-terminatede -uaka --uniqueopções), e, em seguida, em grep(utilizando as opções -zaka --null-datae -caka --count) para contar as linhas de saída.

Diferente da minha versão anterior, não posso usar wcaqui porque requer linhas de entrada terminadas com uma nova linha ... e usar sedou trconverter NULs em novas linhas depois que isso sortanularia o propósito de usar separadores NUL. grep -cfaz um substituto razoável.


Aqui está a mesma coisa reescrita como uma função:

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
cas
fonte
1
Observe que sort -unão retorna elementos exclusivos, mas um de cada conjunto de elementos que são iguais. Por exemplo, ele diria "ok" em ARRAY_DISK_Quantity=(① ②)um sistema GNU onde as localidades geralmente decidem que esses 2 caracteres têm a mesma classificação. Você deseja LC_ALL=C sort -ua exclusividade de byte a byte.
Stéphane Chazelas
apenas outra observação: falhará também, caso nenhum disco adicional seja exibido na CLI, portanto, é necessário também adicionar esta sintaxe
yael
[[`printf"% s \ n "" $ {ARRAY_DISK_Quantity [@]} "| wc -l `-eq` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | grep -c "0" `]] && echo falham
yael 25/12/17
@ StéphaneChazelas vale a pena lidar com a questão da localidade, assim como a questão da IFS. O teste para uma lista vazia é, IMO, melhor feito separadamente - não há necessidade de verificar se há elementos não exclusivos em um conjunto vazio.
cas 25/12
Oi Cas Eu prefiro sua resposta anterior
yael
8

Com zsh:

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

Onde (u)é um sinalizador de expansão de parâmetro para expandir valores exclusivos . Portanto, estamos obtendo uma contagem dos valores exclusivos na matriz.

Substitua == 1por <= 1se você deseja considerar que uma matriz vazia está OK.

Com ksh93, você pode classificar a matriz e verificar se o primeiro elemento é o mesmo que o último:

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

Com ksh88 ou pdksh / mksh:

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

Com bash, você provavelmente precisará de um loop:

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

(funcionaria com todos os shells semelhantes a Bourne com suporte a array (ksh, zsh, bash, yash)).

Observe que ele retorna OK para uma matriz vazia. Adicione um [ "$#" -gt 0 ] || returnno início da função, se você não quiser.

Stéphane Chazelas
fonte
todas essas respostas não parecem apoiar bash?
yael
@yael, veja editar para uma solução bash. Mas por que você usaria bash?
Stéphane Chazelas
No Bash, a página de ajuda para typesetDiz Obsolete. See `help declare'.: existe um motivo para você usá-lo em vez de localou declare?
wjandrea
1
@wjandrea typeseté o que funciona em todas as 4 conchas. É também o original do ksh no início dos anos 80 (o bash copia o ksh88 principalmente quando se trata de definição e declaração de tipo de escopo variável, mas decidiu renomear typeset declaree criar typesetum alias para declarar).
Stéphane Chazelas
4

bash+ awksolução:

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
}

Caso de teste nº 1:

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

Caso de teste nº 2:

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

Eu tenho outra solução somente bash que deve funcionar com seqüências de caracteres também:

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'
}

Demonstração:

[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
Jesse_b
fonte
Observe que os pontos não são válidos nos nomes das funções, embora o Bash seja bastante permissivo. Isso pode causar problemas, como na exportação da função.
wjandrea
2

Com bash e GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi
Cyrus
fonte
Sim, mas e quanto a (10 10 10 10)? Caso contrário, muito bom.
Joe
@ Joe: Boa captura. Eu atualizei minha resposta.
Cyrus
1

Aqui está o POSIX Awk:

awk 'BEGIN {while (++z < ARGC) if (ARGV[z] != ARGV[1]) exit 1}' "${ARRAY_DISK_Quantity[@]}"
Steven Penny
fonte
0

solução somente bash (assumindo que 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
Bhavin Chirag
fonte
Funciona, mas conta todos os erros quando apenas um é suficiente:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe
0

Use um loop for para comparar cada elemento da matriz com o próximo. Finalize o loop uma iteração menor que o comprimento da matriz para evitar a comparação do último elemento com nada no final.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"
megengo
fonte
Bem-vindo ao U&L e obrigado por sua contribuição! Este código imprimirá "Corresponder", mesmo que seja encontrada uma incompatibilidade ... se destina?
fra-san