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
-
So many answers and no votes?jesse_b– jesse_b2017年12月25日 11:59:30 +00:00Commented Dec 25, 2017 at 11:59
-
Will this only be testing integers or should it also test strings?jesse_b– jesse_b2017年12月25日 12:01:49 +00:00Commented Dec 25, 2017 at 12:01
-
I just waiting for the best answer dont worry soon I will voteyael– yael2017年12月25日 12:02:43 +00:00Commented Dec 25, 2017 at 12:02
-
I meant everyone else. This question deserves upvote IMO.jesse_b– jesse_b2017年12月25日 12:03:18 +00:00Commented 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...Display Name– Display Name2017年12月26日 17:35:42 +00:00Commented Dec 26, 2017 at 17:35
8 Answers 8
bash
+ GNU sort
+ GNU grep
solution:
if [ "${#array[@]}" -gt 0 ] && [ $(printf "%s000円" "${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 "%s000円" "$@" |
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
-
1Note 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" onARRAY_DISK_Quantity=(1 2)
on a GNU systems where locales typically decide those 2 characters sort the same. You'd wantLC_ALL=C sort -u
for byte-to-byte uniqueness.Stéphane Chazelas– Stéphane Chazelas2017年12月25日 11:58:51 +00:00Commented 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 syntaxyael– yael2017年12月25日 12:22:04 +00:00Commented 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 failyael– yael2017年12月25日 12:22:08 +00:00Commented 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– cas2017年12月25日 12:53:37 +00:00Commented Dec 25, 2017 at 12:53
-
Hi Cas I prefer your previous answeryael– yael2017年12月25日 13:55:41 +00:00Commented Dec 25, 2017 at 13:55
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.
-
all these answers not seems to support bash ?yael– yael2017年12月25日 12:04:16 +00:00Commented Dec 25, 2017 at 12:04
-
@yael, see edit for a bash solution. But why would you use
bash
?Stéphane Chazelas– Stéphane Chazelas2017年12月25日 12:08:33 +00:00Commented Dec 25, 2017 at 12:08 -
In Bash, the help page for
typeset
saysObsolete. See `help declare'.
Is there a reason you're using it instead oflocal
ordeclare
?wjandrea– wjandrea2017年12月26日 02:52:06 +00:00Commented 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 renametypeset
declare
and maketypeset
an alias to declare).Stéphane Chazelas– Stéphane Chazelas2017年12月26日 07:32:49 +00:00Commented Dec 26, 2017 at 7:32
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
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
-
Note that dots are not valid in function names, though Bash is pretty permissive. This may cause issues like in exporting the function.wjandrea– wjandrea2017年12月26日 02:55:01 +00:00Commented Dec 26, 2017 at 2:55
-
unix.stackexchange.com/a/245953/237982jesse_b– jesse_b2017年12月26日 03:30:58 +00:00Commented Dec 26, 2017 at 3:30
With bash and GNU grep:
if grep -qE '^([0-9]+)( 1円)*$' <<< "${ARRAY_DISK_Quantity[@]}"; then
echo "okay"
else
echo "not okay"
fi
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
-
Works, but counts all errors when just one is enough:
if [ "$ttt" -ne "$i" ]; then res=1; break; fi;
Joe– Joe2017年12月30日 06:30:38 +00:00Commented Dec 30, 2017 at 6:30
Here is POSIX Awk:
awk 'BEGIN {while (++z < ARGC) if (ARGV[z] != ARGV[1]) exit 1}' "${ARRAY_DISK_Quantity[@]}"
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"
-
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– fra-san2019年02月21日 15:10:13 +00:00Commented Feb 21, 2019 at 15:10