如何在bash中测试数组元素是否全部相等?


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

一旦您至少需要某种程度的复杂性,现在便是开始使用一种真正的编程语言的好时机,直到为时已晚…
Display Name

Answers:


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

英文说明:如果对数组的元素进行唯一排序只能得到一个元素,则打印“确定”。否则打印“坏”。

数组上打印有NUL字节,分隔每个元素,通过管道传递到GNU排序(依赖于-zaka --zero-terminated-uaka --unique选项),然后传递给grep(使用-zaka --null-data-caka 选项--count)以计数输出行。

与我以前的版本不同,我不能wc在这里使用它,因为它要求输入行以换行符结尾...并在将无法使用NUL分隔符的目的之后使用sedtrsortNUL 转换为换行符。 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系统上说“ OK”,在该系统上语言环境通常决定这2个字符的排序相同。您需要LC_ALL=C sort -u字节对字节的唯一性。
斯特凡Chazelas

请注意,如果CLI中未显示其他磁盘,也会失败,因此还需要添加此语法
yael

[[`printf“%s \ n”“ $ {ARRAY_DISK_Quantity [@]}” | | wc -l`-eq`printf“%s \ n”“ $ {ARRAY_DISK_Quantity [@]}” | grep -c“ 0”`]] &&回声失败
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

(可以与所有具有数组支持的类似Bourne的外壳一起使用(ksh,zsh,bash,yash))。

请注意,它为空数组返回OK。[ "$#" -gt 0 ] || return如果不希望在函数的开头添加a 。


所有这些答案似乎都不支持bash吗?
yael

@yael,请参见编辑以获取bash解决方案。但是,为什么要使用bash
斯特凡Chazelas

在bash,帮助页typesetObsolete. See `help declare'.有没有你正在使用,而不是它的一个原因local还是declare
wjandrea

1
@wjandrea typeset是适用于所有4个shell的那个。它也是80年代初期来自ksh 的原始版本(bash在变量作用域类型设置和声明时大多复制了ksh88,但决定重命名typeset declare并进行typeset别名声明)。
斯特凡Chazelas

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

我还有另一个bash only解决方案,该解决方案也应该适用于字符串:

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)呢?否则,很好。

@乔:很好。我已经更新了答案。
赛勒斯

1

这是POSIX Awk:

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

0

仅限bash解决方案(假设aARRAY_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,并感谢您的贡献!即使发现不匹配,此代码也将打印“匹配” ...是故意的吗?
fra-san
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.