在 bash 中,带有一元测试的条件表达式-v myvariable
测试变量myvariable
是否已设置。请注意,myvariable
不应通过以美元为前缀来扩展它,所以不是 $myvariable
. 现在我发现对于数组元素,条件表达式-v myarray[index]
也可以很好地工作,而无需完整的扩展语法${myarray[$index]}
。尝试这个:
myarray[2]=myvalue
for i in 1 2 3
do
[ -v myarray\[i] ] && echo element $i is set
done
(注意转义\[
以防止通配符,作为使用引号的替代方法)
给出所需的输出:
element 2 is set
问题 这种行为是否可以安全使用,也就是这种记录在案的行为吗?
附录 阅读 Stéphane Chazelas 的答案https://unix.stackexchange.com/a/677920/376817后,我扩展了我的示例:
myarray[1]=val myarray[2]=val myarray[3]=val myarray[4]=val myarray[5]=val myarray[6]="" myarray[2]=""
unset myarray[3] myarray[4] myarray[5]
touch myarray4 myarrayi
myarray4=val myarrayi=val
然后
for i in {0..7}; do [ -v myarray\[i] ] && echo element $i is set; done
给
element 1 is set
element 2 is set
element 6 is set
不引用或转义索引表达式[i]
:
for i in {0..7}; do [ -v myarray[i] ] && echo element $i is set; done
给
element 0 is set
element 1 is set
element 2 is set
element 3 is set
element 4 is set
element 5 is set
element 6 is set
element 7 is set
与变量myarrayi
unset 相同:
unset myarrayi
for i in {0..7}; do [ -v myarray[i] ] && echo element $i is set; done
给
%nothing%
最后将索引扩展为$i
(仍然没有引用或转义括号):
for i in {0..7}; do [ -v myarray[$i] ] && echo element $i is set; done
它给
element 1 is set
element 2 is set
element 4 is set
因为
ls -l myarray*
节目
-rw-rw-r-- 1 me us 0 nov 17 15:37 myarray4
-rw-rw-r-- 1 me us 0 nov 17 15:37 myarrayi
在
bash
中,您可以执行以下操作:或者
要测试是否设置了索引 2 的数组元素或键“2”的关联数组元素(即使是空字符串),但请注意:
使用
[
(akatest
) 命令时,您需要引用[
and]
字符,因为它们是通配符。由于[
它只是一个普通命令,因此它的解释方式与任何其他普通命令一样,因此a[2]
in 的[ -v a[2] ]
扩展方式与在ls -d a[2]
or中的方式相同unset a[2]
。如果a2
在当前工作目录中调用了一个文件,a[2]
则会扩展到该文件。如果没有,但nullglob
orfailglob
被启用a[2]
将分别扩展为空或给出错误。[[ ... ]]
是一个特殊的结构,有自己的语法,所以不会有问题。对于关联数组(至少在我测试过的版本 5.1 中),如果要检查的键在
$i
变量中,则您希望[ -v 'a[$i]' ]
or[[ -v 'a[$i]' ]]
并且assoc_expand_once
在较新版本中引入的选项bash
不启用。使用or[ -v "a[$i]" ]
不适用于[[ -v a[$i] ]]
$i
包含]
或反斜杠的某些值。在那里,这$
是必须引用的。另请参阅如何在算术表达式中安全地使用关联数组?.仍然对于关联数组,请注意
bash
(与尝试复制的相反ksh93
orbash
)zsh
不支持空键。如果你使用[[ -v 'a[$i]' ]]
and$i
是空字符串,你会得到一个错误。因此,要测试任意键值,请使用[[ -n $i && -v 'a[$i]' ]]
or[ -n "$i" ] && [ -v 'a[$i]' ]
。对于普通(稀疏)数组, in
[ -v 'a[expr]' ]
或[[ -v a[expr] ]]
,expr
被评估为算术表达式。这就是为什么两者都i
起作用$i
。由于算术表达式可能会产生分配变量或运行任意命令的副作用,因此对$i
used in的值[ -v 'a[i]' ]
进行清理非常重要,否则您就有任意命令执行漏洞。正如在 bash/POSIX shells 中忘记引用变量的安全影响中bash
所见, '[ -v lvalue ]
对数组索引起作用的事实使得[ -f $file ]
(作者忘记引用的地方$file
)成为 ACE 漏洞。在任何情况下,您始终可以使用
[ -n "${var+set}" ]
应用于(关联)数组元素的 Bourne/POSIX 方法[ -n "${a[$key]+set}" ]
(或关联数组支持(4.0(2009)或更高版本),并且可以跨外壳移植。请注意,
[ -v var ]
实际上与[ -v 'var[0]' ]
. 就像在 ksh88 中一样,标量变量可以看作是数组索引为 0 的元素。要检查数组或关联数组是否有任何元素(或是否设置了标量变量),您也可以执行
[ "${#a[@]}" -gt 0 ]
or[ -v 'a[@]' ]
或[ -v 'a[*]' ]
.要遍历稀疏数组的索引或关联数组的键,您可以执行以下操作:
(这给出
0
了一个标量变量)。至于这是否记录在案:如果您运行
info bash test
orinfo bash '['
,您会看到它遵循Bash 条件表达式(如内部使用[[ ... ]]
的),其中的文档-v
有:虽然
help test
有:可能是什么
VARNAME
(特别是如果允许数组成员)或者如果VARNAME
引用一个不是标量变量的变量,它会做什么没有明确指定。但是考虑到a[x]
在任何需要变量名的地方通常都允许这样做,并且几十年来一直如此,我们可以放心地假设它在未来仍将如此。如果您查看该官方文档的其他部分,您会发现它通常暗示在任何需要变量名称的地方(无论它被称为NAME、VAR、VARNAME还是更普遍的PARAMETER)
varname[index]
也被接受。例如,在unset
它本身 (info bash unset
) 的文档中,没有提到unset 'array[i]'
,但在关于数组的部分中提到了它。源代码分发(发行说明)中的
NEWS
文件告诉我们,它test -v
是在 bash-4.2(2011)中添加的,可能是受到 ksh93 的启发,不久之前在 ksh93t+(2009,在其自己的发行说明中提到了数组元素支持)在 4.3 中:
在 5.1 中:
您会发现
CWRU/changelog
在源代码分发中提到test -v array[@]
或[[ -v array[$key] ]]
用于关联数组,再次暗示该功能是有意的。将来可以做一些事情来解决上面提到的一些问题,这可能会使我提到的解决方法无效,例如需要引用
$
。