-v
对于“GNU bash,版本 4.2”之前的 Bash 版本,该命令的选项是否有等效的替代方案test
?例如:
shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo
答案1
可移植到所有 POSIX shell:
if [ -n "${foobar+1}" ]; then
echo "foobar is defined"
else
echo "foobar is not defined"
fi
${foobar:+1}
如果您想以foobar
相同的方式处理它,无论它是空的还是未定义的,请这样做。您还可以在 未定义${foobar-}
时使用 来获取空字符串foobar
,否则获取 的值foobar
(或在 后面放置任何其他默认值-
)。
在 ksh 中,iffoobar
已声明但未定义,如 中所示typeset -a foobar
,则${foobar+1}
扩展为空字符串。
Zsh 没有声明但未设置的变量:typeset -a foobar
创建一个空数组。
在 bash 中,数组的行为方式不同且令人惊讶。${a+1}
仅扩展为1
如果a
是非空数组,例如
typeset -a a; echo ${a+1} # prints nothing
e=(); echo ${e+1} # prints nothing!
f=(''); echo ${f+1} # prints 1
同样的原则也适用于关联数组:如果数组变量具有非空索引集,则将其视为已定义的数组变量。
测试是否已定义任何类型的变量的另一种特定于 bash 的方法是检查它是否在.与 不同的是,这会将空数组报告为已定义,但会将已声明但未分配的变量 ( ) 报告为未定义。${!PREFIX*}
${foobar+1}
unset foobar; typeset -a foobar
case " ${!foobar*} " in
*" foobar "*) echo "foobar is defined";;
*) echo "foobar is not defined";;
esac
这相当于typeset -p foobar
测试or的返回值declare -p foobar
,但typeset -p foobar
在已声明但未分配的变量上失败。
在 bash 中,就像在 ksh 中一样,set -o nounset; typeset -a foobar; echo $foobar
在尝试扩展未定义的变量时会触发错误foobar
。与 ksh 不同,set -o nounset; foobar=(); echo $foobar
(或echo "${foobar[@]}"
) 也会触发错误。
请注意,在此处描述的所有情况下,${foobar+1}
当且仅当$foobar
会导致 下的错误时,才展开为空字符串set -o nounset
。
答案2
总结吉尔斯的回答,我制定了以下规则:
- 用于
[[ -v foobar ]]
Bash 版本 >= 4.2 中的变量。 - 用于
declare -p foobar &>/dev/null
Bash 版本 < 4.2 中的数组变量。 - 分别使用
(( ${foo[0]+1} ))
或(( ${bar[foo]+1} ))
作为索引 (-a
) 和键控 (-A
) 数组 (declare
) 的下标。选项 1 和 2 在这里不起作用。
答案3
我对 bash 中的所有变量使用相同的技术,并且它有效,例如:
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"
输出:
foobar is unset
同时
foobar=( "val" "val2" )
[ ${foobar} ] && echo "foobar is set" || echo "foobar is unset"
输出:
foobar is set