¿Cómo probar si los elementos de la matriz son todos iguales en bash?

15

La siguiente matriz representa el número de discos en cada máquina Linux

Cada matriz individual incluye el número de discos en una máquina Linux .

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

¿Cuál es la manera simple de identificar que todos los valores de la matriz son iguales?

Buen estado:

4 4 4 4 4 4 4 4

Mal estado:

4 4 4 4 4 4 2 4

Mal estado:

6 6 6 6 6 6 6 6 6 6 2 6 2
yael
fuente
¿Tantas respuestas y sin votos?
Jesse_b
¿Esto solo probará enteros o también probará cadenas?
Jesse_b
Solo estoy esperando la mejor respuesta, no te preocupes pronto votaré
yael
Me refería a todos los demás. Esta pregunta merece la votación de la OMI.
Jesse_b
una vez que necesite algo de al menos este nivel de complejidad, es un buen momento para comenzar a usar un lenguaje de programación real, hasta que sea demasiado tarde ...
Nombre para mostrar el

Respuestas:

11

bash+ GNU sort+ grepsolución GNU :

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

Explicación en inglés: si la clasificación única de los elementos de la matriz da como resultado un solo elemento, imprima "ok". De lo contrario, imprima "malo".

La matriz se imprime con bytes NUL que separan cada elemento, canalizados en GNU (confiando en -z opciones aka --zero-terminatedy -uaka --unique), y luego en grep(usando las opciones -zaka --null-datay -caka --count) para contar las líneas de salida.

A diferencia de mi versión anterior, no puedo usarlo wcaquí porque requiere líneas de entrada terminadas con una nueva línea ... y usando sedo trpara convertir NUL en nuevas líneas después de que sortse anule el propósito de usar separadores NUL. grep -chace un sustituto razonable.


Aquí está lo mismo reescrito como una función:

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
fuente
1
Tenga en cuenta que sort -uno devuelve elementos únicos, sino uno de cada conjunto de elementos que clasifican lo mismo. Por ejemplo, diría "ok" en ARRAY_DISK_Quantity=(① ②)un sistema GNU donde las configuraciones regionales generalmente deciden que esos 2 caracteres se ordenan de la misma manera. Lo que quiere LC_ALL=C sort -upara la unicidad byte a byte.
Stéphane Chazelas
sólo otra nota será fallan también en caso de que no son discos adicionales se desprende de la CLI así también la necesidad de añadir a esta sintaxis
Yael
[[`` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | wc -l `-eq` printf "% s \ n" "$ {ARRAY_DISK_Quantity [@]}" | grep -c "0" `]] && echo fail
yael
@ StéphaneChazelas vale la pena abordar el problema de la configuración regional, al igual que el problema de IFS. La prueba de una lista vacía, IMO, se realiza mejor por separado: no hay necesidad de buscar elementos no únicos en un conjunto vacío.
cas
Hola Cas, prefiero tu respuesta anterior
yael
8

Con zsh :

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

Dónde (u) hay una bandera de expansión de parámetros para expandir valores únicos . Así que estamos obteniendo un recuento de los valores únicos en la matriz.

Reemplazar == 1 con <= 1si desea considerar que una matriz vacía está bien.

Con ksh93, puede ordenar la matriz y verificar que el primer elemento sea el mismo que el último:

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

Con ksh88 o pdksh / mksh:

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

Con bash, probablemente necesites un bucle:

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

(funcionaría con todos los shells tipo Bourne con soporte de matriz (ksh, zsh, bash, yash)).

Tenga en cuenta que devuelve OK para una matriz vacía. Agregue un [ "$#" -gt 0 ] || returnal comienzo de la función si no quiere eso.

Stéphane Chazelas
fuente
todas estas respuestas no parecen apoyar bash?
yael
@yael, vea editar para una solución bash. ¿Pero por qué usarías bash?
Stéphane Chazelas
En Bash, la página de ayuda typesetdice Obsolete. See `help declare'.¿Hay alguna razón por la que lo estás usando en lugar de localo declare?
wjandrea
1
@wjandrea typesetes la que funciona en los 4 proyectiles. También es el original de ksh a principios de los años 80 (bash copió principalmente ksh88 cuando se trata de la configuración y declaración del tipo de alcance variable, pero decidió cambiar el nombre typeset declarey crear typesetun alias para declarar).
Stéphane Chazelas
4

bash+ awksoltion:

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 prueba n. ° 1:

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

Caso de prueba # 2:

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

Tengo otra solución de bash only que también debería funcionar con cadenas:

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

Demostración:

[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
fuente
Tenga en cuenta que los puntos no son válidos en los nombres de funciones, aunque Bash es bastante permisivo. Esto puede causar problemas como al exportar la función.
wjandrea
2

Con bash y GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi
Ciro
fuente
Sí, pero ¿qué pasa con (10 10 10 10)? De lo contrario, bastante agradable.
Joe
@ Joe: Buena captura. He actualizado mi respuesta.
Cyrus
1

Aquí está POSIX Awk:

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

bash only solution (suponiendo que aes 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
fuente
Funciona, pero los recuentos de todos los errores cuando sólo uno es suficiente:if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe
0

Use un bucle for para comparar cada elemento de la matriz con el siguiente. Finalice el ciclo una iteración menor que la longitud de la matriz para evitar comparar el último elemento con nada al final.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"
megengo
fuente
Bienvenido a U&L y gracias por tu contribución. Este código imprimirá "Coincidencia" incluso si se encuentra una falta de coincidencia ... ¿está destinado?
fra-san