对数组元素进行一元测试 -v

db-*_*inf 8 bash array test

在 bash 中,带有一元测试的条件表达式-v myvariable测试变量是否myvariable已设置。请注意,myvariable不应通过在其前面加上美元前缀来扩展,因此不是 $myvariable。\n现在我发现对于数组元素,条件表达式-v myarray[index]也可以很好地工作,无需完整的扩展语法${myarray[$index]}。尝试这个:

\n
    myarray[2]=myvalue\n    for i in 1 2 3\n    do\n        [ -v myarray\\[i] ] && echo element $i is set\n    done\n
Run Code Online (Sandbox Code Playgroud)\n

(注意转义\\[以防止通配符,作为使用引号的替代方法)

\n

给出所需的输出:

\n
    element 2 is set\n
Run Code Online (Sandbox Code Playgroud)\n

问题\n这种行为可以安全使用吗?又名是这种已记录的行为吗?

\n

附录\n阅读答案后https://unix.stackexchange.com/a/677920/376817后,我扩展了我的示例:

\n
    myarray[1]=val myarray[2]=val myarray[3]=val myarray[4]=val myarray[5]=val myarray[6]="" myarray[2]=""\n    unset myarray[3] myarray[4] myarray[5]\n    touch myarray4 myarrayi\n    myarray4=val myarrayi=val\n
Run Code Online (Sandbox Code Playgroud)\n

然后

\n
    for i in {0..7}; do [ -v myarray\\[i] ] && echo element $i is set; done\n
Run Code Online (Sandbox Code Playgroud)\n

给出

\n
    element 1 is set\n    element 2 is set\n    element 6 is set\n
Run Code Online (Sandbox Code Playgroud)\n

不引用或转义索引表达式[i]

\n
    for i in {0..7}; do [ -v myarray[i] ] && echo element $i is set; done\n
Run Code Online (Sandbox Code Playgroud)\n

给出

\n
    element 0 is set\n    element 1 is set\n    element 2 is set\n    element 3 is set\n    element 4 is set\n    element 5 is set\n    element 6 is set\n    element 7 is set\n
Run Code Online (Sandbox Code Playgroud)\n

与变量 unset 相同myarrayi

\n
    unset myarrayi\n    for i in {0..7}; do [ -v myarray[i] ] && echo element $i is set; done\n
Run Code Online (Sandbox Code Playgroud)\n

给出

\n
    %nothing%\n
Run Code Online (Sandbox Code Playgroud)\n

最后将索引扩展为$i(仍然不引用或转义括号):

\n
    for i in {0..7}; do [ -v myarray[$i] ] && echo element $i is set; done\n
Run Code Online (Sandbox Code Playgroud)\n

它给

\n
    element 1 is set\n    element 2 is set\n    element 4 is set\n
Run Code Online (Sandbox Code Playgroud)\n

因为

\n
    ls -l myarray*\n
Run Code Online (Sandbox Code Playgroud)\n

节目

\n
    -rw-rw-r-- 1 me us 0 nov 17 15:37 myarray4\n    -rw-rw-r-- 1 me us 0 nov 17 15:37 myarrayi\n
Run Code Online (Sandbox Code Playgroud)\n

Sté*_*las 15

在 中bash,您可以执行以下操作:

[ -v 'a[2]' ]
Run Code Online (Sandbox Code Playgroud)

或者

[[ -v a[2] ]]
Run Code Online (Sandbox Code Playgroud)

要测试索引 2 的数组元素或键“2”的关联数组元素是否已设置(即使是空字符串),但请注意:

  • 使用[(又名test)命令时,您需要引用[]字符,因为它们是通配符。由于[是一个普通命令,因此它的解释方式与任何其他普通命令一样,因此a[2]in[ -v a[2] ]将以与ls -d a[2]or相同的方式扩展unset a[2]a2如果当前工作目录中有一个文件被调用,a[2]则会扩展到该文件。如果没有,但启用了nullglob或将分别扩展为空或给出错误。是一个特殊的构造,有自己的语法,所以不会有问题。failgloba[2][[ ... ]]

  • 对于关联数组(至少在我测试过的版本 5.1 中),如果要检查的键位于变量中$i,则您希望启用较新版本中引入的[ -v 'a[$i]' ]or[[ -v 'a[$i]' ]]和选项。对于包含或反斜杠的某些值,使用or不起作用。在那里,必须引用它。另请参阅如何在算术表达式中安全地使用关联数组?assoc_expand_oncebash [ -v "a[$i]" ][[ -v a[$i] ]]$i]$

  • 仍然对于关联数组,请注意bash(与尝试复制或相反ksh93bashzsh支持空键。如果您使用[[ -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 shells 中忘记引用变量的安全影响中所看到的,事实上bash's[ -v lvalue ]适用于数组索引,这导致了诸如[ -f $file ](作者忘记引用$file)ACE 漏洞之类的情况。

  • [ -n "${var+set}" ]在任何情况下,您始终可以使用应用于(关联)数组元素的Bourne/POSIX 方法 ( ):[ -n "${a[$key]+set}" ]它不需要任何解决方法,并且适用于所有支持数组的 ba​​sh 版本(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
    
    Run Code Online (Sandbox Code Playgroud)

    (给出0一个标量变量)。

至于是否有记录:如果您运行info bash testor info bash '[',您会看到它遵循Bash 条件表达式(如 内部使用的[[ ... ]]),其中 的文档-v有:

-v VARNAME
如果设置了 shell 变量 VARNAME(已赋值),则为 True。

虽然help test有:

 -v VAR         True if the shell variable VAR is set.
Run Code Online (Sandbox Code Playgroud)

没有明确指定可能是什么VARNAME(特别是如果允许数组成员)或者如果VARNAME引用不是标量变量的变量它会做什么。但考虑到a[x]只要需要变量名,这种情况通常都是允许的,并且这种情况已经存在了几十年,我们可以放心地假设它在未来仍然会如此。

如果您查看该官方文档的其他部分,您会发现它通常暗示只要需要变量名称(无论是称为NAMEVARVARNAME还是更一般的PARAMETER),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] ]]关联数组,再次暗示该功能是有意为之的。

将来可以采取一些措施来解决上述一些问题,这可能会使我提到的解决方法失效,例如需要引用 ,这并非不可能$