我有一个数组,在脚本运行时会充满不同的错误消息。
我需要一种方法来检查脚本的末尾是否为空,如果有的话,请采取特定的措施。
我已经尝试将其视为普通VAR并使用-z进行检查,但这似乎行不通。有没有一种方法可以检查Bash中数组是否为空?
我有一个数组,在脚本运行时会充满不同的错误消息。
我需要一种方法来检查脚本的末尾是否为空,如果有的话,请采取特定的措施。
我已经尝试将其视为普通VAR并使用-z进行检查,但这似乎行不通。有没有一种方法可以检查Bash中数组是否为空?
Answers:
假设您的数组是$errors
,只需检查元素数是否为零即可。
if [ ${#errors[@]} -eq 0 ]; then
echo "No errors, hooray"
else
echo "Oops, something went wrong..."
fi
set -u
:“ unbound variable”-如果数组为空。
set -u;
foo=();
[ ${#foo[@]} -eq 0 ] && echo empty
。如果为I unset foo
,则显示foo: unbound variable
,但有所不同:数组变量根本不存在,而不是存在且为空。
set -u
-只要您先声明变量,就可以完美运行。
您也可以将数组视为简单变量。这样,只需使用
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" ] && echo empty
,empty
即使array
明显不是空白,它也会打印。
[[ -n "${array[*]}" ]]
将整个数组插入为字符串,并检查其长度是否为非零。如果您认为array=("" "")
是空的,而不是拥有两个空的元素,这可能会很有用。
[[ -n " " ]]
为“ true”,真可惜。您的评论正是我想做的。
set -x
显示了它如何扩展。我想我在发布之前没有测试该评论。>。<您可以通过设置IFS=''
(在此语句周围保存/恢复它)来使其工作,因为"${array[*]}"
扩展使用IFS的第一个字符分隔元素。(如果未设置,则为空格)。但是,“ 如果IFS为null,则在不插入分隔符的情况下连接参数。 ”(用于$ *位置参数的文档,但对于数组,我假设相同)。
我检查了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
如果可能由单个简单命令(请参见简单命令),列表(请参见列表)或复合命令(请参见复合命令)组成的管道(请参见“管道”)返回非零状态,则立即退出。如果失败的命令紧随一段时间或直到关键字,if语句中的测试部分,&&或||中执行的任何命令的一部分之后,命令列表中的一部分,则外壳程序不会退出。列出最后一个&&或||之后的命令,管道中最后一个命令之外的任何命令,或者如果命令的返回状态用!反转。如果除子外壳程序以外的复合命令由于在-e被忽略时命令失败而返回非零状态,则外壳程序不会退出。退出外壳之前,将执行ERR上的陷阱(如果已设置)。
此选项分别适用于外壳环境和每个子外壳环境(请参阅命令执行环境),并且可能导致子外壳在执行子外壳中的所有命令之前退出。
如果复合命令或shell函数在忽略-e的上下文中执行,则即使设置了-e且命令返回a故障状态。如果复合命令或shell函数在忽略-e的上下文中执行时设置了-e,则该设置在复合命令或包含该函数调用的命令完成之前不会生效。
-u
执行参数扩展时,将特殊参数“ @”或“ *”以外的未设置变量和参数视为错误。错误消息将被写入标准错误,并且非交互式外壳将退出。
如果不需要,请随时省略:+${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
@
肯定是。您可以使用*
类似的数组扩展[ "${array[*]}" ]
,可以吗?不过,[[
也可以。对于具有多个空字符串的数组,这两种方式的行为都令人惊讶。这两个[ ${#array[*]} ]
和[[ "${array[@]}" ]]
是假的array=()
和array=('')
,但真正的array=('' '')
(两个或两个以上空字符串)。如果您希望一个或多个空字符串全部为true,则可以使用[ ${#array[@]} -gt 0 ]
。如果您希望它们全部为假,则可以将//
它们淘汰。
[ "${array[*]}" ]
,但是如果遇到这种表达,对我来说很难理解它的作用。由于[...]
根据字符串对插值结果进行运算。与相对[[...]]
,它可以知道所插值的内容。也就是说,它可以知道它已传递给数组。[[ ${array[@]} ]]
对我来说是“检查数组是否为非空”,而[ "${array[*]}" ]
作为“检查所有数组元素的插值结果是否为非空字符串”。
[ ${#array[*]} ]
,您可能是想说的[ "${array[*]}" ]
,因为前者适用于任何数量的元素。因为元素数总是非空字符串。对于具有两个元素的后者,括号内的表达式扩展' '
为非空字符串。至于[[ ${array[@]} ]]
,他们只是(正确地认为)两个元素的任何数组都是非空的。
如果要检测带有空元素的数组,如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而不是使用子外壳,因为您不能轻易地从子外壳中获得结果。
# 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字符。
arr=(" ")
。
就我而言,第二个答案还不够,因为可能会有空格。我来了:
if [ "$(echo -ne ${opts} | wc -m)" -eq 0 ]; then
echo "No options"
else
echo "Options found"
fi
echo | wc
与使用Shell内置程序相比,似乎不必要地效率低下。
[ ${#errors[@]} -eq 0 ];
以解决空白问题的方式修改第二个答案?我也希望内置。
$#
扩展为一个数字,即使在之后也可以正常工作opts+=("")
。例如unset opts;
opts+=("");opts+=(" "); echo "${#opts[@]}"
,我得到2
。您可以举一个不起作用的例子吗?
opts=("")
其视为与opts=()
?这不是一个空数组,但是您可以使用来检查是否有空数组或第一个元素为空opts=("");
[[ "${#opts[@]}" -eq 0 || -z "$opts" ]] && echo empty
。请注意,您当前的回答是对表示“无选择” opts=("" "-foo")
,这完全是伪造的,这会重现该行为。 您可能会[[ -z "${opts[*]}" ]]
猜到要将所有数组元素插值到一个平面字符串中,该字符串-z
检查非零长度。 如果检查第一个元素已足够,则-z "$opts"
可以使用。
我更喜欢使用双括号:
if [[ !${array[@]} ]]
then
echo "Array is empty"
else
echo "Array is not empty"
fi
双括号:https : //stackoverflow.com/questions/669452/is-preferable-over-in-bash
=
是一个字符串运算符。在这种情况下,它可以正常工作,但是我会改用正确的算术运算符-eq
(以防万一我想切换到-ge
or-lt
等)。