14

The following array represented the numbers of disks on each linux machines

Each single array includes the number of disks on a linux machine.

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

what is the simple way to identify that all array's values are equal?

Good status:

4 4 4 4 4 4 4 4

Bad status:

4 4 4 4 4 4 2 4

Bad status:

6 6 6 6 6 6 6 6 6 6 2 6 2
5
  • So many answers and no votes?
    – jesse_b
    Dec 25, 2017 at 11:59
  • Will this only be testing integers or should it also test strings?
    – jesse_b
    Dec 25, 2017 at 12:01
  • I just waiting for the best answer dont worry soon I will vote
    – yael
    Dec 25, 2017 at 12:02
  • I meant everyone else. This question deserves upvote IMO.
    – jesse_b
    Dec 25, 2017 at 12:03
  • once you need something of at least this level of complexity, it's a good time to start using a real programming language, until it's too late… Dec 26, 2017 at 17:35

8 Answers 8

12

bash + GNU sort + GNU grep solution:

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

English explanation: if unique-sorting the elements of the array results in only one element, then print "ok". Otherwise print "bad".

The array is printed with NUL bytes separating each element, piped into GNU sort (relying on the -z aka --zero-terminated and -u aka --unique options), and then into grep (using options -z aka --null-data and -c aka --count) to count the output lines.

Unlike my previous version, I can't use wc here because it requires input lines terminated with a newline...and using sed or tr to convert NULs to newlines after the sort would defeat the purpose of using NUL separators. grep -c makes a reasonable substitute.


Here's the same thing rewritten as a function:

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
8
  • 1
    Note that sort -u doesn't return unique elements but one of each set of elements that sort the same. For instance, it would say "ok" on ARRAY_DISK_Quantity=(① ②) on a GNU systems where locales typically decide those 2 characters sort the same. You'd want LC_ALL=C sort -u for byte-to-byte uniqueness. Dec 25, 2017 at 11:58
  • just another note it will be fail also in case no additional disks are appears from CLI so need also to add this syntax
    – yael
    Dec 25, 2017 at 12:22
  • [[ ` printf "%s\n" "${ARRAY_DISK_Quantity[@]}" | wc -l ` -eq ` printf "%s\n" "${ARRAY_DISK_Quantity[@]}" | grep -c "0" ` ]] && echo fail
    – yael
    Dec 25, 2017 at 12:22
  • @StéphaneChazelas the locale issue is worth dealing with, as is the IFS issue. Testing for an empty list is, IMO, best done separately - there's no need to check for non-unique elements in an empty set.
    – cas
    Dec 25, 2017 at 12:53
  • Hi Cas I prefer your previous answer
    – yael
    Dec 25, 2017 at 13:55
9

With zsh:

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

Where (u) is a parameter expansion flag to expand unique values. So we're getting a count of the unique values in the array.

Replace == 1 with <= 1 is you want to consider an empty array is OK.

With ksh93, you could sort the array and check that the first element is the same as the last:

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

With ksh88 or pdksh/mksh:

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

With bash, you'd probably need a 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

(would work with all the Bourne-like shells with array support (ksh, zsh, bash, yash)).

Note that it returns OK for an empty array. Add a [ "$#" -gt 0 ] || return at the start of the function if you don't want that.

4
  • all these answers not seems to support bash ?
    – yael
    Dec 25, 2017 at 12:04
  • @yael, see edit for a bash solution. But why would you use bash? Dec 25, 2017 at 12:08
  • In Bash, the help page for typeset says Obsolete. See `help declare'. Is there a reason you're using it instead of local or declare?
    – wjandrea
    Dec 26, 2017 at 2:52
  • 1
    @wjandrea typeset is the one that works in all 4 shells. It's also the original one from ksh in the early 80s ( bash mostly copied ksh88 when it comes to variable scoping type setting and declaration but decided to rename typeset declare and make typeset an alias to declare). Dec 26, 2017 at 7:32
4

bash + awk soltion:

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
}

Test case #1:

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

Test case #2:

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

I have another bash only solution that should work with strings as well:

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

Demonstration:

[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
2
2

With bash and GNU grep:

if grep -qE '^([0-9]+)( \1)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then 
  echo "okay"
else
  echo "not okay"
fi
2
  • Yes, but what about (10 10 10 10)? Otherwise, quite nice.
    – Joe
    Dec 30, 2017 at 6:20
  • @Joe: Good catch. I've updated my answer.
    – Cyrus
    Dec 30, 2017 at 6:39
0

bash only solution (assuming a is 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
1
  • Works, but counts all errors when just one is enough: if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
    – Joe
    Dec 30, 2017 at 6:30
0

Here is POSIX Awk:

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

Use a for loop to compare each array element to the next. End the loop one iteration less than the length of the array to avoid comparing the last element to nothing at the end.

for (( i=0; i<((${#array[@]}-1)); i++ )); do
    [ "${array[$i]}" != "${array[(($i+1))]}" ] && echo "Mismatch"
done
echo "Match"
1
  • Welcome on U&L and thank you for your contribution! This code will print "Match" even if a mismatch is found... is it intended?
    – fra-san
    Feb 21, 2019 at 15:10

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.