我有一个数组,该数组在脚本运行时会充满不同的错误消息。

我需要一种方法来检查它是否为空,而不是在脚本末尾,如果有的话,请采取特定的措施。

我已经尝试将其视为普通VAR,并使用-z进行检查,但这似乎行不通。在Bash中是否可以检查数组是否为空?

#1 楼

假设您的数组是$errors,只需检查元素数是否为零即可。

if [ ${#errors[@]} -eq 0 ]; then
    echo "No errors, hooray"
else
    echo "Oops, something went wrong..."
fi


评论


请注意,=是字符串运算符。在这种情况下,它可以正常工作,但是我会改用正确的算术运算符-eq(以防万一我想切换到-ge或-lt等)。

–音乐爱好者
13年8月8日在19:27

不适用于set -u:“未绑定变量”-如果数组为空。

–user97619
17-4-8在19:56



@Igor:在Bash 4.4中为我工作。设置-u; foo =(); [$ {#foo [@]} -eq 0] &&回显为空。如果我未设置foo,则它会输出foo:未绑定变量,但这是不同的:数组变量根本不存在,而不是存在且为空。

– Peter Cordes
17 Sep 17'4:22



在使用set -u时也在Bash 3.2(OSX)中进行了测试-只要您先声明了变量,它就可以很好地工作。

– zeroimpl
18年8月5日在16:48

#2 楼

在这种情况下,我通常使用算术扩展:

if (( ${#a[@]} )); then
    echo not empty
fi


评论


干净整洁!我喜欢。我还注意到,如果数组的第一个元素始终为非空,则(($ {#a}))(第一个元素的长度)也将起作用。但是,这将在a =('')上失败,而答案中给出的(($ $ ## a [@]}))将成功。

– cxw
18年8月15日在12:36



可能曾经是这样,但是我无法复制它。如果echo $ BASH_VERSION && a =('')&&((($ {#a [@]})))&& echo不为空|| echo empty恰好输出“ empty”,随意告诉您的版本。

–x-yuri
20 Dec 17 '21:38

#3 楼

您也可以将数组视为简单变量。这样,只需使用

if [ -z "$array" ]; then
    echo "Array empty"
else
    echo "Array non empty"
fi


或使用另一面

if [ -n "$array" ]; then
    echo "Array non empty"
else
    echo "Array empty"
fi


该解决方案的问题是如果这样声明数组:array=('' foo)。这些检查将报告该数组为空,而显然不是。 (感谢@musiphil!)

使用[ -z "$array[@]" ]显然也不是解决方案。未指定大括号会尝试将$array解释为字符串(在这种情况下,[@]是简单的文字字符串),因此始终报告为false:“文字字符串[@]是否为空?”显然不是。

评论


[-z“ $ array”]或[-n“ $ array”]不起作用。尝试array =(''foo); [-z“ $ array”] &&回显为空,即使array很明显不是空的,它也会打印为空。

–音乐爱好者
15年8月17日在23:25

[[-n“ $ {array [*]}”]]]将整个数组插入为字符串,并检查其长度是否为非零。如果您认为array =(“”“”)为空,而不是拥有两个空元素,那么这可能会很有用。

– Peter Cordes
17年9月19日在15:21

@PeterCordes我认为不行。该表达式的结果为单个空格字符,[[--n“”]]为“ true”,真可惜。您的评论正是我想做的。

–迈克尔
19年6月18日在16:37

@迈克尔:胡扯,你是对的。它仅适用于1元素的空字符串数组,而不适用于2元素。我什至检查了较旧的bash,但那里还是错误的。就像您说的那样,set -x显示了它如何扩展。我想我在发布之前没有测试该评论。 >。<您可以通过设置IFS =''使它起作用(在此语句周围保存/还原),因为“ $ {array [*]}”扩展将元素与IFS的第一个字符分隔开。 (如果未设置,则为空格)。但是,“如果IFS为空,则无需中间分隔符即可连接参数。” (用于$ *位置参数的文档,但对于数组我假设相同)。

– Peter Cordes
19年6月18日在16:57



@Michael:添加了执行此操作的答案。

– Peter Cordes
19年6月18日在17:35

#4 楼

我用bash-4.4.0进行了检查:
#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]} ]]; then
        echo not empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

bash-4.1.5
#!/usr/bin/env bash
set -eu
check() {
    if [[ ${array[@]:+${array[@]}} ]]; then
        echo non-empty
    else
        echo empty
    fi
}
check   # empty
array=(a b c d)
check   # not empty
array=()
check   # empty

在后一种情况下,您需要以下结构:
${array[@]:+${array[@]}}

在空数组或未设置数组上失败。那是如果您像我通常那样做。这提供了更严格的错误检查。在文档中:

-e
如果管道(请参阅管道)(可能由单个简单命令(请参见简单命令),列表(请参见列表))或复合命令(请参见复合命令)返回非零状态。如果失败的命令紧随一段时间或直到关键字,if语句中的测试部分,&&或||中执行的任何命令的一部分之后,命令列表立即出现,则外壳程序不会退出。列出除了最后一个&&或||之后的命令,管道中除最后一个命令之外的任何命令,或者该命令的返回状态用!反转。如果除子外壳程序以外的复合命令由于在-e被忽略时命令失败而返回非零状态,则外壳程序不会退出。 ERR陷阱(如果已设置)在外壳程序退出之前执行。
此选项分别适用于外壳程序环境和每个子外壳程序环境(请参阅命令执行环境),并且可能导致子外壳程序在执行其中的所有命令之前退出。
如果在忽略-e的上下文中执行复合命令或shell函数,则即使-e设置,复合命令或函数体内执行的所有命令都不会受到-e设置的影响。设置并且命令返回失败状态。如果复合命令或shell函数在忽略-e的上下文中执行时设置了-e,则该设置在复合命令或包含函数调用的命令完成之前不会生效。
-u
执行参数扩展时,将特殊参数“ @”或“ *”以外的未设置变量和参数视为错误。错误消息将被写入标准错误,并且非交互式外壳将退出。

如果不需要,请忽略set -eu部件。
也请注意,那么在这里使用:+${array[@]}运算符非常重要,通过[[您将获得:
$ cat 1.sh
#!/usr/bin/env bash
set -eu
array=(a b c d)
if [ "${array[@]}" ]; then
    echo non-empty
else
    echo empty
fi

$ ./1.sh
_/1.sh: line 4: [: too many arguments
empty


评论


使用-u时,您实际上应该使用$ {array [@] +“ $ {array [@]}”} cf stackoverflow.com/a/34361807/1237617

–雅库布·博琴斯基(Jakub Bochenski)
17年9月13日在14:52

@JakubBochenski您正在谈论哪个版本的bash? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9

–x-yuri
17年9月13日在18:28

单括号示例中的问题肯定是@。您可以使用*数组扩展,例如[“ $ {array [*]}”“],不是吗?不过,[[也可以正常工作。对于具有多个空字符串的数组,这两种方式的行为都令人惊讶。 [$ {#array [*]}]和[[“ $ {array [@]}”]]]对于array =()和array =('')均为假,但对于array =('''')为true (两个或多个空字符串)。如果希望一个或多个空字符串全部为true,则可以使用[$ {#array [@]} -gt 0]。如果您希望它们全部为假,则可以//将它们淘汰。

–eisd
18-09-2在6:42

@eisd我可以使用[“ $ {array [*]}”]],但是如果遇到这种表达式,对我来说很难理解它的作用。由于内插结果是根据字符串进行运算的。与[[]]相对,后者可以知道所插值的内容。也就是说,它可以知道它已传递给数组。 [[$ {array [@]}]]对我来说是“检查数组是否为非空”,而[“ $ {array [*]}”]对我来说是“检查所有数组元素的内插结果是否为非空字符串”。

–x-yuri
'18 Sep 2'在15:30



...对于有两个空字符串的行为,这一点都不令我感到惊讶。令人惊讶的是一个空字符串的行为。但是可以说是合理的。关于[$ {#array [*]}],您可能是指[“ $ {array [*]}”]],因为前者对于任意数量的元素都是正确的。因为元素数总是非空字符串。对于带有两个元素的后者,括号内的表达式扩展为'',这是非空字符串。至于[[$ {array [@]}]],他们只是认为(正确地)两个元素的任何数组都是非空的。

–x-yuri
18/09/2在15:46



#5 楼

如果要检测包含空元素的数组,例如arr=("" "")为空,则与arr=()相同,可以将所有元素粘贴在一起并检查结果是否为零长度。 (如果数组可能很大,则构建数组内容的展平副本对于性能而言不是理想的选择。但是希望您不对诸如此类的程序使用bash ...)

但是"${arr[*]}"可以扩展为由IFS的第一个字符分隔的元素。因此,您需要保存/恢复IFS并执行IFS=''才能完成此工作,否则请检查字符串长度==数组元素的数量-1。(n元素数组具有n-1分隔符)。要一一处理,最简单的方法是用1

arr=("" "")

## Assuming default non-empty IFS
## TODO: also check for ${#arr[@]} -eq 0
concat="${arr[*]} "      # n-1 separators + 1 space + array elements
[[ "${#concat}" -ne "${#arr[@]}" ]]  && echo not empty array || echo empty array


set -x

### a non-empty element
$ arr=("" "x")
  + arr=("" "x")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat=' x '
  + [[ 3 -ne 2 ]]
  + echo not empty array
not empty array

### 2 empty elements
$ arr=("" "")
  + arr=("" "")
$ concat="${arr[*]} ";  [[ "${#concat}" -ne "${#arr[@]}" ]] && echo not empty array || echo empty array
  + concat='  '
  + [[ 2 -ne 2 ]]
  + echo empty array
empty array

不幸的是,这对于arr=()失败:[[ 1 -ne 0 ]]。因此,您需要先分别检查是否为空数组。


或使用IFS=''。也许您想保存/还原IFS而不是使用子Shell,因为您无法轻易地从子Shell中获得结果。

# inside a () subshell so we don't modify our own IFS
(IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)


示例:

$ arr=("" "")
$ (IFS='' ; [[ -n "${arr[*]}" ]] && echo not empty array || echo empty array)
   + IFS=
   + [[ -n '' ]]
   + echo empty array
empty array


可与arr=()一起使用-它仍然只是空字符串。

评论


我赞成,但是我已经开始使用[[“ $ {arr [*]}” = * [![:space:]] *]],因为我可以指望至少一个非WS字符。

–迈克尔
19年6月18日在18:30

@Michael:是的,如果您不需要拒绝arr =(“”),那是一个不错的选择。

– Peter Cordes
19年6月18日在20:59

#6 楼

我更喜欢使用双括号:

if [[ !${array[@]} ]]
then
    echo "Array is empty"
else
    echo "Array is not empty"
fi



双括号:https://stackoverflow.com/questions/669452/is-preferable-over-in -bash


#7 楼

就我而言,第二个答案还不够,因为可能会有空格。我来了:

 if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
  echo "No options"
else
  echo "Options found"
fi
 


评论


回声与使用shell内置程序相比,wc似乎不必要地效率低下。

– Peter Cordes
17/09/17在4:16



不知道我是否了解@PeterCordes,是否可以修改第二个答案'[$ {#errors [@]} -eq 0];以某种方式解决空白问题?我也希望内置。

– Micha
17年9月18日在13:10



空格到底是怎么引起问题的? $#扩展为一个数字,即使在opts + =(“”)之后也可以正常工作。例如未选择opts + =(“”); opts + =(“”);回显“ $ {#opts [@]}”,我得到2。您能显示一个不起作用的示例吗?

– Peter Cordes
17年9月18日在13:33

很久以前了IIRC原始源始终至少打印“”。因此,对于opts =“”或opts =(“”),我需要0,而不是1,忽略空的换行符或空字符串。

– Micha
17年9月19日在14:20



好的,因此您需要将opts =(“”)与opts =()相同?那不是一个空数组,但是您可以使用opts =(“”);检查是否有空数组或第一个元素为空。 [[“ $ {#opts [@]}” -eq 0 || -z“ $ opts”]] && echo空。请注意,您当前的答案对opts =(“”“ -foo”)说“无选项”,这完全是虚假的,并且可以重现该行为。您可以[[-z“ $ {opts [*]}”]]]插入所有数组元素到一个平面字符串中,-z检查是否为非零长度。如果检查第一个元素已足够,则-z“ $ opts”有效。

– Peter Cordes
17年9月19日在15:18