如何使用 nounset shell 选项测试 4.2 之前版本的 Bash 中是否定义了变量?

如何使用 nounset shell 选项测试 4.2 之前版本的 Bash 中是否定义了变量?

-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

总结吉尔斯的回答,我制定了以下规则:

  1. 用于[[ -v foobar ]]Bash 版本 >= 4.2 中的变量。
  2. 用于declare -p foobar &>/dev/nullBash 版本 < 4.2 中的数组变量。
  3. 分别使用(( ${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

相关内容