在 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
问题 这种行为可以安全使用吗又名这是有记录的行为吗?
附录 看完答案后https://unix.stackexchange.com/a/677920/376817Stéphane Chazelas 的例子,我扩展了我的例子:
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
与变量 unset 相同myarrayi
:
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
答案1
在 中bash
,您可以执行以下操作:
[ -v 'a[2]' ]
或者
[[ -v a[2] ]]
要测试索引 2 的数组元素或键“2”的关联数组元素是否已设置(即使是空字符串),但请注意:
使用
[
(又名test
)命令时,您需要引用[
和]
字符,因为它们是通配符。由于[
是一个普通命令,因此它的解释方式与任何其他普通命令一样,因此 ina[2]
将以[ -v a[2] ]
与ls -d a[2]
or相同的方式扩展unset a[2]
。如果a2
当前工作目录中有一个文件被调用,a[2]
则会扩展到该文件。如果没有,但启用了nullglob
或将分别扩展为空或给出错误。是一个特殊的构造,有自己的语法,所以不会有问题。failglob
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
)bash
不zsh
支持空键。如果您使用[[ -v 'a[$i]' ]]
and$i
是空字符串,则会收到错误。因此,要测试任意键值,请使用[[ -n $i && -v 'a[$i]' ]]
或[ -n "$i" ] && [ -v 'a[$i]' ]
。对于普通(稀疏)数组, in
[ -v 'a[expr]' ]
或[[ -v a[expr] ]]
,expr
被计算为算术表达式。这就是i
和都$i
起作用的原因。由于算术表达式可能会产生分配变量或运行任意命令的副作用,因此对$i
使用的值[ -v 'a[i]' ]
进行清理非常重要,否则您将面临任意命令执行漏洞。正如所见忘记在 bash/POSIX shell 中引用变量的安全隐患,事实上bash
's[ -v lvalue ]
适用于数组索引,这导致了诸如[ -f $file ]
(作者忘记引用$file
)ACE 漏洞之类的事情。[ -n "${var+set}" ]
在任何情况下,您始终可以使用应用于(关联)数组元素的Bourne/POSIX 方法 ( ):[ -n "${a[$key]+set}" ]
它不需要任何解决方法,并且适用于所有支持数组的 bash 版本(2.0 (1996) 或更高版本)或关联数组支持(4.0 (2009) 或更高版本),并且可以在 shell 之间移植。请注意,这
[ -v var ]
实际上与 相同[ -v 'var[0]' ]
。与 ksh88 中一样,标量变量可以视为数组索引 0 的元素。要检查数组或关联数组是否有任何元素(或设置了标量变量),您还可以执行
[ "${#a[@]}" -gt 0 ]
or[ -v 'a[@]' ]
or[ -v 'a[*]' ]
。2023年编辑, 作为由@johnraff 指出,自版本 5.2 起,后两个不再适用于关联数组,除非启用 5.1 兼容性。要循环稀疏数组的索引或关联数组的键,您可以执行以下操作:
for key in "${!a[@]}"; do printf 'The element of key "%s" is set\n' "$key" done
(给出
0
一个标量变量)。
至于是否有记录:如果您运行info bash test
or info bash '['
,您将看它遵循Bash 条件表达式(如内部使用的[[ ... ]]
),其中 的文档-v
有:
-v VARNAME
如果设置了 shell 变量 VARNAME(已赋值),则为 True。
虽然help test
有:
-v VAR True if the shell variable VAR is set.
没有明确指定可能是什么VARNAME
(特别是如果允许数组成员)或者如果VARNAME
引用不是标量变量的变量它会做什么。但考虑到a[x]
只要需要变量名,这种情况通常都是允许的,并且这种情况已经存在了几十年,我们可以放心地假设它在未来仍然会如此。
如果您查看该官方文档的其他部分,您会发现它通常暗示在任何需要变量名的地方(无论它是否被称为姓名,VAR,变量名或者更一般地说范围),varname[index]
也被接受。例如,在unset
其自身的文档中(info bash unset
),没有提到unset 'array[i]'
,但在关于数组的部分。
这NEWS
源代码发行版(发行说明)中的文件告诉我们,它test -v
是在 bash-4.2 (2011) 中添加的,可能是受到 ksh93 的启发,ksh93 不久前在 ksh93t+ 中添加了它(2009 年,在其自己的发行说明中提到了数组元素支持)
F。
test
//[
有[[
一个新的-v
变量一元运算符,如果已设置“variable”则返回成功。
在 4.3 中:
二。
test
//[
二元运算符[[
-v variable
现在可以理解数组引用。
在 5.1 中:
X。
test -v N
现在可以测试是否设置了位置参数 N。
您会发现CWRU/changelog
在源代码分发中提到了关联数组test -v array[@]
或[[ -v array[$key] ]]
关联数组,再次暗示该功能是有意为之的。
将来可以采取一些措施来解决上述一些问题,这可能会使我提到的解决方法失效,例如需要引用 ,这并非不可能$
。
答案2
在 bash 5.2 中,不再可能测试关联数组是否包含任何带有 的元素test -v 'arr[@]'
,现在测试专门名为“@”的键。
我想test "${#arr[@]}" -gt 0
或者(( ${#k[@]} > 0 ))
会这样做。
参见compat51:https://www.gnu.org/software/bash/manual/bash.html#Shell-Compatibility-Mode