如何测试是否在4.2版之前的Bash中使用名词集外壳程序选项定义了变量?


16

对于“ GNU bash,版本4.2”之前的Bash版本-v,该test命令的选项是否具有等效的替代品?例如:

shopt -os nounset
test -v foobar && echo foo || echo bar
# Output: bar
foobar=
test -v foobar && echo foo || echo bar
# Output: foo

-v不是的选项test,而是条件表达式的运算符。
所有StackExchange

@Tim除了作为标记,字符串和行的一部分外,还有三件事:An option到命令test -v,an operator到a conditional expressionunary test primaryfor [ ]。不要将英语与外壳定义混在一起。

Answers:


36

可移植到所有POSIX外壳:

if [ -n "${foobar+1}" ]; then
  echo "foobar is defined"
else
  echo "foobar is not defined"
fi

作出这样的${foobar:+1},如果你想治疗foobar以同样的方式是否为空或没有定义。${foobar-}foobarundefined 时,您还可以使用来获取空字符串,foobar否则使用值(或在后面放置其他任何默认值-)。

在ksh中,如果foobar像一样在声明但未定义typeset -a foobar,则${foobar+1}扩展为空字符串。

Zsh没有声明但未设置的变量:typeset -a foobar创建一个空数组。

在bash中,数组的行为方式不同而令人惊讶。${a+1}仅扩展为1if 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或的返回值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


1
数组呢?在Bash版本“ GNU bash,版本4.1.10(4)-发行版(i686-pc-cygwin)” echo "${foobar:+1}"中,1如果declare -a foobar先前已发行,则不会打印,因此foobar是一个索引数组。declare -p foobar正确报告declare -a foobar='()'。是否"${foobar:+1}"只适用于非数组变量的工作?
蒂姆·弗里斯克

如果您对“ defined”的定义为“ would work under ” ,则@TimFriske ${foobar+1}(不带:,我在原始答案中倒了两个例子)对于bash中的数组是正确的。如果您的定义不同,bash有点奇怪。看到我更新的答案。$foobarset -o nounset
吉尔斯(Gilles)'所以

1
关于“在bash中,数组的行为以不同且令人惊讶的方式”。可以在bash(1)联机帮助页的“数组”部分中解释此行为。它指出“引用不带下标的数组变量等效于引用带下标0的数组”。因此,如果没有0为定义索引或键a=(),则${a+1}正确不返回任何内容。
蒂姆·弗里斯克

1
@TimFriske我知道bash实现符合其文档。但是将空数组视为未定义变量确实是一种奇怪的设计。
吉尔斯(Gilles)'所以

我更喜欢以下结果:如何检查Bash中是否设置了变量?-> 正确的方法 ...我对应该如何工作感到很共鸣。我敢说,Windows有一个defined运算符。 Test 可以做到这一点;它不可能是困难的( ......)

5

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

  1. 使用[[ -v foobar ]]中的Bash版本> = 4.2变量。
  2. 使用declare -p foobar &>/dev/null中的Bash版本<4.2数组变量。
  3. 分别对索引()和键控()数组()的下标使用(( ${foo[0]+1} ))或。选项1和2在这里不起作用。(( ${bar[foo]+1} ))-a-Adeclare

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

如果数组具有多个值,则必须删除[@]。
Stein Inge Morisbak

1
只要您要测试它是否具有值,而不是是否已定义,就可以很好地工作。即,foobar=""然后将进行报告foobar is unset。等等,我把它拿回来。实际上,仅测试第一个元素是否为空,因此,如果您知道变量不是数组,并且只关心空性而不是定义性,那似乎只是一个好主意。
罗恩·伯克

仅在脚本运行时允许使用未定义的变量(不设置-u)时才有效
Florian Heigl
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.