我如何在Bash中知道一个变量是否已经设置?
例如,我如何检查用户是否提供了函数的第一个参数?
function a {
# if $1 is set ?
}
我如何在Bash中知道一个变量是否已经设置?
例如,我如何检查用户是否提供了函数的第一个参数?
function a {
# if $1 is set ?
}
if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
${var+x}
是一个参数展开,如果 var
未设置,则计算结果为空,否则替换为字符串 x
。
可以省略引号(因此我们可以说 ${var+x}
而不是 "${var+x}"
),因为这种语法和用法保证只会展开为不需要引号的内容(因为它要么展开为 x
(它不包含单词间断,因此不需要引号),要么展开为空(这将导致 [ -z ]
,方便地计算出与 [ -z "" ]
相同的值(true))。
[ -z "${var+x}" ]
,即使会付出微不足道的O(1)速度惩罚成本。该解决方案的第一作者还在使用此解决方案的代码旁添加了评论,并提供了指向此答案的URL,现在该答案还包括了为什么可以安全省略引号的解释。
if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi
var=''
,那么上述解决方案将输出“var为空白”。[ -z "$var" ]
就足够了。${var+x}
是正确的替换方式。使用 [ -z ${var:+x} ]
和使用 [ -z "$var" ]
没有任何区别。 - Graeme[ -z "" -a -z ${var+x} ]
会出现bash: [: argument expected
的错误提示(但在zsh中则不会)。我非常不喜欢使用某些情况下恰好有效的语法来回答问题。 - FauxFaux[ -z $var ]
与省略引号一样,并不更加“错误”。无论哪种方式,你都在对输入作出假设。如果你可以将空字符串视为未设置的内容,那么[ -z $var ]
就足够了。 - nshew13检查非空/非零字符串变量,即如果设置了,则使用
if [ -n "$1" ]
这是与-z
相反的选项。我发现自己使用-n
的频率比使用-z
更高。
你可以像这样使用它:
if [ -n "$1" ]; then
echo "You supplied the first parameter!"
else
echo "First parameter not supplied."
fi
[[ ]]
而不是[ ]
,因为在某些情况下[[ ]]
更加强大且会引起较少的问题(有关两者之间区别的解释,请参见[此问题](https://dev59.com/O3A75IYBdhLWcg3wMl8Z))。该问题明确要求使用*bash*解决方案,并未提及任何可移植性要求。 - Flow[]
在非 bash 的脚本中表现更好。 - Hengjieif [ ! -z "$1" ]
。 - The Godfather以下是如何测试参数是否未设置、为空或已设置值的方法:
+--------------------+----------------------+-----------------+-----------------+
| Expression | parameter | parameter | parameter |
| in script: | Set and Not Null | Set But Null | Unset |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word} | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word | assign word |
| ${parameter=word} | substitute parameter | substitute null | assign word |
| ${parameter:?word} | substitute parameter | error, exit | error, exit |
| ${parameter?word} | substitute parameter | substitute null | error, exit |
| ${parameter:+word} | substitute word | substitute null | substitute null |
| ${parameter+word} | substitute word | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+
来源:POSIX:参数扩展:
在所有显示为“替换”(substitute)的情况下,表达式都将被替换为所示的值。在所有显示为“赋值”(assign)的情况下,参数都将被赋以该值,该值也将替换表达式。
为了演示这种操作:
+--------------------+----------------------+-----------------+-----------------+
| Expression | When FOO="world" | When FOO="" | unset FOO |
| in script: | (Set and Not Null) | (Set But Null) | (Unset) |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello} | world | hello | hello |
| ${FOO-hello} | world | "" | hello |
| ${FOO:=hello} | world | FOO=hello | FOO=hello |
| ${FOO=hello} | world | "" | FOO=hello |
| ${FOO:?hello} | world | error, exit | error, exit |
| ${FOO?hello} | world | "" | error, exit |
| ${FOO:+hello} | hello | "" | "" |
| ${FOO+hello} | hello | hello | "" |
+--------------------+----------------------+-----------------+-----------------+
set foo; echo ${1:-set but null or unset}
会输出 "foo";set --; echo ${1:-set but null or unset}
则会输出 "set but null"。 - Jensset
命令设置。 - Jens: ${FOOBAR:=/etc/foobar.conf}
来为变量设置默认值,如果变量未设置或为空,则使用默认值。 - Jensparameter
是任何变量名称。word
是根据您使用表格中的哪种语法以及变量 $parameter
是否设置、设置但为空还是未设置而进行替换的某个字符串。不要使事情更加复杂,但是 word
也可以包括变量!这对于通过字符分隔可选参数非常有用。例如:${parameter:+,${parameter}}
如果它被设置但不为空,则输出逗号分隔的 ,$parameter
。在这种情况下,word
是 ,${parameter}
。 - TrinitronX虽然这里列出的大部分技巧是正确的,但Bash 4.2支持一种实际检测变量存在性的测试(man bash),而不是测试变量的值。
[[ -v foo ]]; echo $?
# 1
foo=bar
[[ -v foo ]]; echo $?
# 0
foo=""
[[ -v foo ]]; echo $?
# 0
值得注意的是,在set -u
/ set -o nounset
模式下使用此方法检查未设置的变量时,不会引起错误,这与许多其他方法不同,例如使用[ -z
。
[[ -v aaa ]]; echo $?
==>
-bash: 条件二进制运算符预期
-bash:'aaa'附近有语法错误
- Dan123
,在这种情况下,我更喜欢使用[[ ${foo-123} -eq 123 ]]
来压缩 [[ -v foo && $foo -eq 123 ]]
。 - Amit Naidu有很多方法可以实现这个需求,以下是其中一种方法:
if [ -z "$1" ]
如果$1为null或未设置,则此操作成功。
foo=""
,$foo
有一个值,但 -z
只会报告它是空的。如果你使用了 set -o nounset
,那么 -z $foo
将会崩溃。 - Keith Thompsonfoo=
),那么Russel的回答是正确的。 - Flow我总觉得在其他答案中的POSIX表格不易理解,所以这是我的解释:
参数扩展 | VARIABLE 已设置 |
VARIABLE 为空 |
VARIABLE 未设置 |
---|---|---|---|
${VARIABLE-default} |
$VARIABLE |
"" |
"default" |
${VARIABLE=default} |
$VARIABLE |
"" |
$(VARIABLE="default") |
${VARIABLE?default} |
$VARIABLE |
"" |
退出码为127 |
${VARIABLE+default} |
"default" |
"default" |
"" |
${VARIABLE:-default} |
$VARIABLE | "默认值" |
"默认值" |
${VARIABLE:=默认值} |
$VARIABLE |
$(VARIABLE="默认值") |
$(VARIABLE="默认值") |
${VARIABLE:?默认值} |
$VARIABLE |
exit 127 |
exit 127 |
${VARIABLE:+默认值} |
"默认值" |
"" |
"" |
请注意,每个组(有和没有前置冒号的)都具有相同的设置和取消设置情况,因此唯一的区别在于如何处理空情况。
使用前置冒号时,空和未设置情况是相同的,因此尽可能使用这些情况(即使用:=
,而不仅仅是=
,因为空情况是不一致的)。
标题:
VARIABLE
非空(VARIABLE="something"
)VARIABLE
为空/ null(VARIABLE=""
)VARIABLE
不存在 (unset VARIABLE
)值:
$VARIABLE
表示结果是变量的原始值。"default"
表示结果是提供的替换字符串。""
表示结果为空(一个空字符串)。exit 127
表示脚本以退出代码127停止执行。$(VARIABLE="default")
表示结果是"default"
,并且VARIABLE
(之前为空或未设置)还将设置为"default"
。VARIABLE =“”
可以写成VARIABLE =
。但前者更易读。 - Palec$(VARIABLE="default")
表示结果为"default"
,并且VARIABLE
也将被设置为"default"
。 - Tomerrexit
,这掩盖了真正的退出状态。 - deizel.为了判断变量是否不为空,我使用
if [[ $var ]]; then ... # `$var' expands to a nonempty string
相反的测试检查一个变量是否未设置或为空:
if [[ ! $var ]]; then ... # `$var' expands to the empty string (set or not)
我使用以下方法来判断一个变量是否已设置(为空或非空):
if [[ ${var+x} ]]; then ... # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ... # Parameter 1 exists (empty or nonempty)
相反的测试是检测一个变量是否未设置:
if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ... # We were called with no arguments
[ $isMac ] && param="--enable-shared"
。 - user1985657${variable+x}
来获取x
,当且仅当$variable
被设置),而且它有更多详细的解释为什么是正确的。 - Mark Haferkamp由于标签为bash
,因此我将提供一个重点关注Bash的答案。
只要您在Bash中处理命名变量,即使它是一个空数组,此函数也应始终告诉您变量是否已设置。
variable-is-set() {
declare -p "$1" &>/dev/null
}
在Bash中(至少从3.0版本开始),如果var
是一个已声明/设置的变量,那么declare -p var
输出一个declare
命令,该命令将变量var
设置为其当前类型和值,并返回状态码0
(成功)。如果var
未声明,则declare -p var
将向stderr
输出错误消息,并返回状态码1
。使用&>/dev/null
,将普通的stdout
和stderr
输出重定向到/dev/null
,永远不会被看到,并且不改变状态码。因此,该函数只返回状态码。
[ -n "$var" ]
: 这只检查${var[0]}
是否非空。(在Bash中,$var
与${var[0]}
相同。)
[ -n "${var+x}" ]
: 这只检查${var [0]}
是否已设置。[ "${#var[@]}" != 0 ]
: 这只检查$var
的至少一个索引是否已设置。这仅适用于命名变量(包括$_
),而不是某些特殊变量($!
,$@
,$#
,$$
,$*
,$?
,$-
,$0
,$1
,$2
等等,以及我可能忘记的任何变量)。 由于这些都不是数组,所以基于POSIX风格的[ -n "${var+x}" ]
对所有这些特殊变量都有效。但要小心将其包装在函数中,因为许多特殊变量在调用函数时会更改值/存在性。
typeset -p
而不是declare -p
。我读到ksh仅支持前者,但无法测试。我知道Bash 3.0+和Zsh 5.5.1都支持typeset -p
和declare -p
,只是在另一个上提供替代。但我没有测试除这两个关键字之外的差异,也没有测试其他shell。[ -n "${var+x}" ]
有效。
var
,eval
传递的代码,运行测试以确定eval
的代码是否设置了var
,最后显示不同测试的结果状态码。
我跳过test -v var
,[ -v var ]
和[[ -v var ]]
因为它们产生与POSIX标准的[ -n "${var+x}" ]
相同的结果,而需要Bash 4.2+。我还跳过typeset -p
,因为在我测试过的shell(Bash 3.0到5.0和Zsh 5.5.1)中它与declare -p
相同。
is-var-set-after() {
# Set var by passed expression.
unset var
eval "$1"
# Run the tests, in increasing order of accuracy.
[ -n "$var" ] # (index 0 of) var is nonempty
nonempty=$?
[ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
plus=$?
[ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
count=$?
declare -p var &>/dev/null # var has been declared (any type)
declared=$?
# Show test results.
printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}
请注意,由于Bash将非数字数组索引视为“0”(如果变量未声明为关联数组),因此测试结果可能出乎意料。另外,关联数组仅在Bash 4.0+中有效。
# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo" # 0 0 0 0
is-var-set-after "var=(foo)" # 0 0 0 0
is-var-set-after "var=([0]=foo)" # 0 0 0 0
is-var-set-after "var=([x]=foo)" # 0 0 0 0
is-var-set-after "var=([y]=bar [x]=foo)" # 0 0 0 0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''" # 1 0 0 0
is-var-set-after "var=([0]='')" # 1 0 0 0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')" # 1 1 0 0
is-var-set-after "var=([1]=foo)" # 1 1 0 0
is-var-set-after "declare -A var; var=([x]=foo)" # 1 1 0 0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()" # 1 1 1 0
is-var-set-after "declare -a var" # 1 1 1 0
is-var-set-after "declare -A var" # 1 1 1 0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var" # 1 1 1 1
标题行中的测试助记符对应于[ -n "$var" ]
,[ -n "${var+x}" ]
,[ "${#var[@]}" != 0 ]
和declare -p var
。
test: -n +x #@ -p
var=foo: 0 0 0 0
var=(foo): 0 0 0 0
var=([0]=foo): 0 0 0 0
var=([x]=foo): 0 0 0 0
var=([y]=bar [x]=foo): 0 0 0 0
var='': 1 0 0 0
var=([0]=''): 1 0 0 0
var=([1]=''): 1 1 0 0
var=([1]=foo): 1 1 0 0
declare -A var; var=([x]=foo): 1 1 0 0
var=(): 1 1 1 0
declare -a var: 1 1 1 0
declare -A var: 1 1 1 0
unset var: 1 1 1 1
declare -p var &>/dev/null
自Bash 3.0版本以来,对于测试命名变量是(100%?)可靠的。
[ -n "${var+x}" ]
在符合POSIX标准的情况下是可靠的,但无法处理数组。declare -p var &>/dev/null
) 能正常工作。谢谢! - user1387866declare var
声明了一个变量,但它并没有在set -o nounset
方面设置它:使用declare foo bar =; set -o nounset; echo $bar; echo $foo
进行测试。 - xebeche/
!我已经修复了,但是考虑到 @xebeche 的评论,我认为这个逻辑足够混乱,需要一个函数来处理。@xebeche 这对我的答案很不方便...看起来我得尝试一些像 declare -p "$1" | grep =
或者 test -v "$1"
这样的东西。 - Mark Haferkampset -o nounset; (echo "$var") &>/dev/null
与 [ -n "${var+x}" ]
具有相同的真值,因此唯一的区别是 set -o nounset
导致脚本/子shell退出。话虽如此,我认为我的答案应该解释变量被 设置 和仅仅 声明 的区别,而不是混淆这些情况。请随意编辑。在下一个永久通知之前,我不会回到这个答案。 - Mark Haferkampdeclare
无法工作;您可以使用${var[idx]+x}
来实现。您还可以使用"${var[*]+x}"
,但在某些情况下会失败,这一点与#@
相同;请注意,"${var[@]+x}"
很奇怪,因为它会扩展为多个单词,在这种情况下是0,从而改变了所调用的test
形式(如果您使用[[
而不是[
则没有问题)。我认为test -v
的优点在于它是唯一适用于整个变量及其元素的方法。 - o11cif [ "$1" != "" ]; then
echo \$1 is set
else
echo \$1 is not set
fi
虽然在处理参数时,通常最好测试$#,即参数的数量,但这是我的个人观点。
if [ $# -gt 0 ]; then
echo \$1 is set
else
echo \$1 is not set
fi
[ "$1" != "" ]
(或者 [ -n "$1" ]
)... - Gordon Davisson$1
被设置为空字符串,这将会失败。 - HelloGoodbye$1
设置为空字符串时工作的有用解决方法。 - Mark Berryset -o nounset
,这将失败。 - Flimm[[ -n $1 ]] && FOO=$1
从bash中获取脚本参数未设置FOO变量。而使用[[ "$1" != "" ]] && FOO=$1
则可以正常工作。bash 4.2.46(2)-release (x86_64-redhat-linux-gnu)。 - gaoithe在现代版本的Bash(我认为是4.2或更高版本;我不确定),我会尝试这样做:
if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
echo "Variable is set to an empty string"
else
echo "Variable is set to some string"
fi
[ -v "$VARNAME" ]
不是错误的写法,只是进行了不同的测试。假设 VARNAME=foo
,那么它检查是否存在一个名为 foo
的变量并且被赋值了。 - chepner-v
不符合 POSIX 标准。 - kzh