题 检查Bash中的数组是否为空


我有一个数组,当我的脚本运行时,它会填充不同的错误消息。

我需要一种方法来检查脚本末尾是否为空,如果是,则执行特定操作。

我已经尝试过将其视为普通VAR并使用-z来检查它,但这似乎不起作用。有没有办法在Bash中检查数组是否为空?

谢谢


82
2018-02-11 03:59






答案:


假设您的阵列是 $errors,只需检查元素的数量是否为零。

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

113
2018-02-11 04:10



请注意 = 是一个字符串运算符。在这种情况下它恰好工作正常,但我会使用正确的算术运算符 -eq 相反(以防万一我想切换到 -ge 要么 -lt等)。 - musiphil
不适用 set -u:“unbound variable” - 如果数组为空。 - Igor
@Igor:在Bash 4.4中为我工作。 set -u;  foo=();  [ ${#foo[@]} -eq 0 ] && echo empty。如果我 unset foo,然后它打印 foo: unbound variable,但那是不同的:数组变量根本不存在,而不是存在并且是空的。 - Peter Cordes
使用时也在Bash 3.2(OSX)中进行了测试 set -u  - 只要你先声明你的变量,这就完美了。 - zeroimpl


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

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:“是文字字符串 [@] 空的?“显然不是。


6
2018-06-23 10:29



[ -z "$array" ] 要么 [ -n "$array" ] 不起作用。尝试 array=('' foo); [ -z "$array" ] && echo empty,它会打印出来 empty 即使 array 显然不是空的。 - musiphil
[[ -n "${array[*]}" ]] 将整个数组插入为字符串,检查非零长度。如果你考虑 array=("" "") 为空,而不是有两个空元素,这可能是有用的。 - Peter Cordes


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

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

3
2017-08-02 06:04



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


我查了一下 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[@]}}

因为它在空或未设置的数组上没有失败。如果你这样做的话 set -eu 像我通常那样。这提供了更严格的错误检查。从 文档

-e

如果管道(请参阅管道)(可能包含单个简单命令(请参阅简单命令)),列表(请参阅列表)或复合命令(请参阅复合命令)返回非零状态,请立即退出。如果失败的命令是紧跟在while或until关键字之后的命令列表的一部分,if语句中的测试的一部分,在&&或||中执行的任何命令的一部分,则shell不会退出list除了最后的&&或||之后的命令,管道中的任何命令但是最后一个命令,或者命令的返回状态是否被反转!如果子shell以外的复合命令返回非零状态,因为在忽略-e时命令失败,则shell不会退出。 ERR上的陷阱(如果已设置)将在shell退出之前执行。

此选项分别应用于shell环境和每个子shell环境(请参阅命令执行环境),并可能导致子shell在执行子shell中的所有命令之前退出。

如果复合命令或shell函数在忽略-e的上下文中执行,则复合命令或函数体中执行的任何命令都不会受-e设置的影响,即使设置了-e并且命令返回失败状态。如果复合命令或shell函数在忽略-e的上下文中执行时设置-e,则在复合命令或包含函数调用的命令完成之前,该设置将不起作用。

-u

在执行参数扩展时,将特殊参数“@”或“*”以外的未设置变量和参数视为错误。将向标准错误写入错误消息,并退出非交互式shell。

如果您不需要,请随意省略 :+${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

2
2017-12-04 14:58



同 -u 你应该实际使用 ${array[@]+"${array[@]}"} 比照 stackoverflow.com/a/34361807/1237617 - Jakub Bochenski
@JakubBochenski你在谈论哪个版本的bash? gist.github.com/x-yuri/d933972a2f1c42a49fc7999b8d5c50b9 - x-yuri
单括号示例中的问题是 @,当然。你可以用 * 数组扩展如 [ "${array[*]}" ]你能不能吗?仍然, [[ 也行得很好。对于具有多个空字符串的数组,这两者的行为有点令人惊讶。都 [ ${#array[*]} ] 和 [[ "${array[@]}" ]] 是假的 array=() 和 array=('') 但是真实的 array=('' '') (两个或多个空字符串)。如果你想要一个或多个空字符串都给出true,你可以使用 [ ${#array[@]} -gt 0 ]。如果你想要它们都是假的,你也许可以 // 他们出去了。 - eisd
@eisd我可以使用 [ "${array[*]}" ]但如果我遇到这样的表达,我就更难理解它的作用。以来 [...] 根据插值结果的字符串进行操作。相反 [[...]],可以知道内插的内容。也就是说,它可以知道它是通过一个数组。 [[ ${array[@]} ]] 读给我的是“检查数组是否为非空”,而 [ "${array[*]}" ] as“检查所有数组元素的插值结果是否为非空字符串”。 - x-yuri
......对于有两个空字符串的行为,对我来说并不奇怪。令人惊讶的是一个空字符串的行为。但可以说是合理的。关于 [ ${#array[*]} ],你可能意味着 [ "${array[*]}" ]因为前者适用于任何数量的元素。因为元素的数量总是非空字符串。关于后者有两个元素,括号内的表达式扩展为 ' ' 这是非空字符串。至于 [[ ${array[@]} ]],他们只是认为(并且正确地说)两个元素的任何数组都是非空的。 - x-yuri


在我的情况下, 第二个答案 还不够,因为可能有空格。我跟着:

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

0
2018-02-17 19:54



echo | wc 与使用shell内置函数相比,它看起来毫无效率。 - Peter Cordes
不确定我是否理解@PeterCordes,我可以修改第二个答案吗? [ ${#errors[@]} -eq 0 ]; 在解决空白问题的方法?我也更喜欢内置的。 - Micha
空白究竟是如何导致问题的? $# 扩展到一个数字,即使之后也能正常工作 opts+=("")。例如 unset opts;  opts+=("");opts+=(" "); echo "${#opts[@]}" 我明白了 2。你能展示一些不起作用的例子吗? - Peter Cordes
这是很久以前的事了。 IIRC的始发源始终至少打印“”。因此,对于opts =“”或opts =(“”),我需要0而不是1,忽略空换行符或空字符串。 - Micha
好的,所以你需要治疗 opts=("") 同样的 opts=()?这不是一个空数组,但你可以检查空数组或空第一个元素 opts=("");  [[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty。请注意,您当前的答案显示“无选项” opts=("" "-foo"),这完全是虚假的,这再现了这种行为。 你可以 [[ -z "${opts[*]}" ]] 我想,将所有数组元素插入到一个扁平字符串中,这就是 -z 检查非零长度。  如果检查第一个元素就足够了 -z "$opts" 作品。 - Peter Cordes


我更喜欢使用双括号:

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

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


0
2017-11-04 06:40