提问人:prosseek 提问时间:8/30/2010 最后编辑:Peter Mortensenprosseek 更新时间:5/1/2023 访问量:2221214
如何检查是否在 Bash 中设置了变量
How to check if a variable is set in Bash
答:
if [ "$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" ]
$1
$1
set -o nounset
[[ -n $1 ]] && FOO=$1
[[ "$1" != "" ]] && FOO=$1
有很多方法可以做到这一点,其中一种是:
if [ -z "$1" ]
如果 $1 为 null 或未设置,则此操作成功。
评论
[[ ]]
只不过是一个可移植性的陷阱。 应该在这里使用。if [ -n "$1" ]
foo=""
$foo
-z
-z $foo
set -o nounset
foo=
您可以执行以下操作:
function a {
if [ ! -z "$1" ]; then
echo '$1 is set'
fi
}
评论
-n
-z
$1
[ ! -z "$1" ]
[[ ! -z $1 ]]
$1
要检查非空/非零字符串变量,即如果设置了,请使用
if [ -n "$1" ]
这与 相反。我发现自己使用的不仅仅是 .-z
-n
-z
您可以像这样使用它:
if [ -n "$1" ]; then
echo "You supplied the first parameter!"
else
echo "First parameter not supplied."
fi
评论
[[ ]]
[ ]
[[ ]]
[]
if [ ! -z "$1" ]
case "$1" in
"") echo "blank";;
*) echo "set"
esac
评论
$1
要检查变量是否设置为非空值,请使用 ,如其他人已经指出的那样。[ -n "$x" ]
大多数情况下,最好将具有空值的变量与未设置的变量相同。但是,如果需要,您可以区分两者:( 扩展为 if is set 和空字符串 if is unset)。[ -n "${x+set}" ]
"${x+set}"
set
x
x
要检查参数是否已传递,请 test ,这是传递给函数(或脚本,当不在函数中时)的参数数(参见 Paul 的回答)。$#
阅读手册页的“参数扩展”部分。参数扩展不会为正在设置的变量提供常规测试,但如果未设置参数,您可以对参数执行多项操作。bash
例如:
function a {
first_arg=${1-foo}
# rest of the function
}
如果被赋值,将设置为等于,否则它使用值“foo”。如果绝对必须采用单个参数,并且不存在良好的默认值,则可以在未给出参数时退出并显示错误消息:first_arg
$1
a
function a {
: ${1?a must take a single argument}
# rest of the function
}
(请注意,使用 as 作为 null 命令,它只是扩展其参数的值。在此示例中,我们不想做任何事情,如果未设置,则退出):
$1
评论
: ${var?message}
要检查是否设置了变量:
var=""; [[ $var ]] && echo "set" || echo "not set"
评论
$var
(通常)正确的方式
if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
其中 是一个参数扩展,如果未设置,则计算结果为空,否则替换字符串。${var+x}
var
x
引言题外话
引号可以省略(所以我们可以说代替),因为这种语法和用法保证它只会扩展到不需要引号的东西(因为它要么扩展到(不包含分词符,所以它不需要引号),要么什么都不用(这导致 ,它方便地计算为相同的值(true))。${var+x}
"${var+x}"
x
[ -z ]
[ -z "" ]
然而,虽然引号可以安全地省略,而且对所有人来说都不是很明显的(对于这个引号解释的第一作者来说,他也是一个主要的 Bash 编码员甚至都不明显),但有时最好用引号写成 ,以非常小的 O(1) 速度损失的代价。第一作者还将其添加为使用此解决方案的代码旁边的注释,该解决方案提供了此答案的 URL,现在还包括对为什么可以安全地省略引号的解释。[ -z "${var+x}" ]
(经常)错误的方式
if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi
这通常是错误的,因为它不区分未设置的变量和设置为空字符串的变量。也就是说,如果 ,那么上面的解将输出“var is blank”。var=''
在用户必须指定扩展名或其他属性列表的情况下,unset 和“设置为空字符串”之间的区别是必不可少的,并且不指定它们默认为非空值,而指定空字符串应使脚本使用空扩展名或附加属性列表。
不过,这种区别可能并非在所有情况下都是必不可少的。在这些情况下会很好。[ -z "$var" ]
评论
${var+x}
[ -z ${var:+x} ]
[ -z "$var" ]
[ -z "" -a -z ${var+x} ]
bash: [: argument expected
[ -z $var ]
[ -z $var ]
要查看变量是否为非空,我使用
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"
${variable+x}
x
$variable
如果你想测试一个变量是绑定的还是未绑定的,这很有效,即使你打开了名词集选项:
set -o noun set
if printenv variableName >/dev/null; then
# variable is bound to a value
else
# variable is unbound
fi
评论
set -o nounset
set -o noun set
export
if (set -u; : $var) 2>/dev/null
if (: ${var?}) 2>/dev/null
下面介绍如何测试参数是否未设置、为空(“Null”)或设置有值:
+--------------------+----------------------+-----------------+-----------------+
| 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}
set --; echo ${1:-set but null or unset}
set
: ${FOOBAR:=/etc/foobar.conf}
parameter
是任何变量名称。 是要替换的某个字符串,具体取决于您使用的表中的语法,以及变量是已设置、已设置但为 null 还是未设置。不要让事情变得更复杂,但也可以包括变量!这对于传递由字符分隔的可选参数非常有用。例如:如果设置了逗号,则输出逗号分隔,但未为 null。在本例中,是word
$parameter
word
${parameter:+,${parameter}}
,$parameter
word
,${parameter}
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi
if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi
评论
$1
,并且 up to 始终处于设置状态。对不起,这是失败的。$2
$N
虽然这里提到的大多数技术都是正确的,但 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: conditional binary operator expected
-bash: syntax error near 'aaa'
[[ -v foo ]]
[[ -v $foo ]]
123
[[ ${foo-123} -eq 123 ]]
[[ -v foo && $foo -eq 123 ]]
在现代版本的 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
-v
[: -v: unexpected operator
bash
sh
我总是使用这个,因为任何第一次看到代码的人都很容易理解它:
if [ "$variable" = "" ]
then
echo "Variable X is empty"
fi
而且,如果要检查是否为空;
if [ ! "$variable" = "" ]
then
echo "Variable X is not empty"
fi
就是这样。
评论
[[ ... ]]
[ ... ]
启用 Bash 选项时,上述答案不起作用。此外,它们不是动态的,例如,如何测试名称为“dummy”的变量?试试这个:set -u
is_var_defined()
{
if [ $# -ne 1 ]
then
echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
exit 1
fi
# Tricky. Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
# is defined with this construct: [ ! -z "$var" ]. Instead, we must use default value
# substitution with this construct: [ ! -z "${var:-}" ]. Normally, a default value follows the
# operator ':-', but here we leave it blank for empty (null) string. Finally, we need to
# substitute the text from $1 as 'var'. This is not allowed directly in Bash with this
# construct: [ ! -z "${$1:-}" ]. We need to use indirection with eval operator.
# Example: $1="var"
# Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
# Code execute: [ ! -z ${var:-} ]
eval "[ ! -z \${$1:-} ]"
return $? # Pedantic.
}
相关新闻: 在 Bash 中,如何测试变量是否在“-u”模式下定义
评论
eval "[ ! -z \${$1:-} ]"
[ ! -z ${!1:-} ];
如果您想检查中的任何内容,我找到了一个(更好的)代码来执行此操作。$@
if [[ $1 = "" ]] then echo '$1 is blank' else echo '$1 is filled up' fi
为什么会这样?中的所有内容都存在于 Bash 中,但默认情况下它是空白的,因此无法帮助您。$@
test -z
test -n
更新:您还可以计算参数中的字符数。
if [ ${#1} = 0 ] then echo '$1 is blank' else echo '$1 is filled up' fi
评论
[[ $foo ]]
或
(( ${#foo} ))
或
let ${#foo}
或
declare -p foo
在 shell 中,如果字符串的长度为零,则可以使用运算符,该运算符为 True。-z
如果未设置,则使用简单的单行代码来设置默认值,否则可以选择显示以下消息:MY_VAR
[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
使用是了解变量是否已设置的最简单方法,但该选项不区分未设置的变量和设置为空字符串的变量:[[ -z "$var" ]]
-z
$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset"
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset
最好根据变量的类型进行检查:环境变量、参数或常规变量。
对于 env 变量:
[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"
对于参数(例如,检查参数是否存在):$5
[[ $# -ge 5 ]] && echo "Set" || echo "Unset"
对于正则变量(使用辅助函数,以一种优雅的方式完成):
function declare_var {
declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"
笔记:
$#
:提供位置参数的数量。declare -p
:给出作为参数传递的变量的定义。如果存在,则返回 0,否则返回 1 并打印错误消息。&> /dev/null
:禁止输出,而不影响其返回代码。declare -p
我的首选方式是这样的:
$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set
所以基本上,如果一个变量被设置了,它就变成了“结果的否定”(what will be = “is set”)。false
true
而且,如果它未设置,它将成为“结果的否定”(正如空结果的计算结果所计算的那样)(因此将以 being = “NOT set”结束)。true
true
false
这是我每天使用的:
#
# Check if a variable is set
# param1 name of the variable
#
function is_set() { [[ $(eval echo "\${${1}+x}") ]]; }
这在 Linux 和 Solaris 到 Bash 3.0 下运行良好。
$ myvar="TEST"
$ is_set myvar ; echo $?
0
$ myvar=
$ is_set myvar ; echo $?
0
$ unset myvar
$ is_set myvar ; echo $?
1
评论
test -n "$(eval "echo "\${${1}+x}"")"
[[ ${!1+x} ]]
eval
[[
isset() { [ -n "$1" ] && [ -n "$(eval printf '%s' "\${$1+x}" || true)" ]; }
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi
评论
在 Bash 中,您可以在内置的-v
[[ ]]
#! /bin/bash -u
if [[ ! -v SOMEVAR ]]; then
SOMEVAR='hello'
fi
echo $SOMEVAR
评论
注意
由于标签,我给出了一个以 Bash 为重点的答案。bash
简答
只要你只在 Bash 中处理命名变量,这个函数就应该总是告诉你变量是否已设置,即使它是一个空数组。
variable-is-set() {
declare -p "$1" &>/dev/null
}
为什么这样做
在 Bash 中(至少可以追溯到 3.0),if 是一个声明/设置的变量,然后输出一个命令,将变量设置为其当前类型和值的任何类型,并返回状态代码(成功)。如果未声明,则向 输出错误消息并返回状态代码。使用 ,将常规和输出重定向到 ,永远不会被看到,并且不更改状态代码。因此,该函数仅返回状态代码。var
declare -p var
declare
var
0
var
declare -p var
stderr
1
&>/dev/null
stdout
stderr
/dev/null
为什么其他方法(有时)在 Bash 中失败
[ -n “$var” ]
:这仅检查是否为非空。(在 Bash 中,与 相同。${var[0]}
$var
${var[0]}
[ -n “${var+x}” ]
:这仅检查是否设置了。${var[0]}
[ “${#var[@]}” != 0 ]
:这仅检查是否至少设置了一个索引。$var
当此方法在 Bash 中失败时
这仅适用于命名变量(包括),不适用于某些特殊变量(、由于这些都不是数组,因此 POSIX 样式适用于所有这些特殊变量。但要注意不要将其包装在函数中,因为许多特殊变量在调用函数时会更改值/存在。$_
$!
$@
$#
$$
$*
$?
$-
$0
$1
$2
[ -n "${var+x}" ]
Shell 兼容性说明
如果您的脚本具有数组,并且您正在尝试使其与尽可能多的 shell 兼容,请考虑使用 而不是 .我读到ksh只支持前者,但无法测试这一点。我确实知道 Bash 3.0+ 和 Zsh 5.5.1 都支持 和 ,区别仅在于其中一个是另一个的替代品。但是我没有测试过这两个关键字之外的差异,也没有测试过其他 shell。typeset -p
declare -p
typeset -p
declare -p
如果你需要你的脚本与 POSIX sh 兼容,那么你就不能使用数组。没有数组,可以工作。[ -n "${var+x}" ]
Bash 中不同方法的比较代码
此函数取消设置变量,传递代码,运行测试以确定是否由 d 代码设置,最后显示不同测试的结果状态代码。var
eval
var
eval
我跳过了 ,因为它们产生与 POSIX 标准相同的结果,同时需要 Bash 4.2+。我也跳过了,因为它与我测试过的 shell(Bash 3.0 到 5.0 和 Zsh 5.5.1)相同。test -v var
[ -v var ]
[[ -v var ]]
[ -n "${var+x}" ]
typeset -p
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 中的命名变量是可靠的(100%?),至少从 3.0 开始。
[ -n “${var+x}” ]
在符合 POSIX 的情况下是可靠的,但不能处理数组。- 存在其他测试,用于检查变量是否为非空,以及检查其他 shell 中声明的变量。但这些测试既不适合 Bash 脚本,也不适合 POSIX 脚本。
评论
declare -p var &>/dev/null
declare -p PATH &> /dev/null
declare -p ASDFASDGFASKDF &> /dev/null
declare var
set -o nounset
declare foo bar=; set -o nounset; echo $bar; echo $foo
/
declare -p "$1" | grep =
test -v "$1"
set -o nounset; (echo "$var") &>/dev/null
[ -n "${var+x}" ]
set -o nounset
我喜欢辅助函数来隐藏 Bash 的粗糙细节。在这种情况下,这样做会增加更多(隐藏的)粗糙度:
# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
local varname="$1"
! [ -z ${!varname+x} ]
}
因为我在这个实现中第一次遇到了错误(受到 Jens 和 Lionel 答案的启发),所以我想出了一个不同的解决方案:
# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
declare -p $1 &>/dev/null
}
我发现它更直接、更害羞、更容易理解/记住。测试用例显示它是等价的:
function main()
{
declare -i xyz
local foo
local bar=
local baz=''
IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"
IsDeclared xyz; echo "IsDeclared xyz: $?"
IsDeclared foo; echo "IsDeclared foo: $?"
IsDeclared bar; echo "IsDeclared bar: $?"
IsDeclared baz; echo "IsDeclared baz: $?"
}
main
测试用例还表明它没有声明 var(除非后跟 '=')。在很长一段时间里,我以为我以这种方式声明变量,只是现在发现我只是表达了我的意图......我猜这是一个无操作。local var
IsDeclared_Tricky xyz: 1 IsDeclared_Tricky foo: 1 IsDeclared_Tricky bar: 0 IsDeclared_Tricky baz: 0 IsDeclared xyz:1 IsDeclared foo:1 IsDeclared 柱线:0 IsDeclared baz:0
奖励:用例
我主要使用这个测试以一种有点“优雅”和安全的方式(几乎类似于接口)为函数提供(和返回)参数:
# Auxiliary functions
function die()
{
echo "Error: $1"; exit 1
}
function assertVariableDeclared()
{
IsDeclared "$1" || die "variable not declared: $1"
}
function expectVariables()
{
while (( $# > 0 )); do
assertVariableDeclared $1; shift
done
}
# Actual example
function exampleFunction()
{
expectVariables inputStr outputStr
outputStr="$inputStr, World!"
}
function bonus()
{
local inputStr='Hello'
local outputStr= # Remove this to trigger the error
exampleFunction
echo $outputStr
}
bonus
如果在声明所有必需变量的情况下调用:
世界您好!
还:
错误:未声明变量:outputStr
检查变量是否已声明/未设置的函数
包括空的$array=()
以下函数测试给定名称是否作为变量存在
# The first parameter needs to be the name of the variable to be checked.
# (See example below)
var_is_declared() {
{ [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}
var_is_unset() {
{ [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;}
}
- 通过首先测试变量是否设置(未)设置,可以避免对声明的调用(如果不需要)。
- 但是,如果包含空的名称,则调用声明将确保我们得到正确的结果
$1
$array=()
- 传递给 /dev/null 的数据永远不会太多,因为只有在变量未设置或空数组时才会调用 claim。
此函数将按照以下条件进行测试:
a; # is not declared a=; # is declared a="foo"; # is declared a=(); # is declared a=(""); # is declared unset a; # is not declared a; # is unset a=; # is not unset a="foo"; # is not unset a=(); # is not unset a=(""); # is not unset unset a; # is unset
.
更多细节
和测试脚本,请参阅我对“如何检查 bash 中是否存在变量?
备注:Peregring-lk 的回答也表明了类似的用法,这确实是巧合。否则我当然会相信它!declare -p
对于那些希望在使用 set -u
的脚本中检查未设置或为空的用户:
if [ -z "${var-}" ]; then
echo "Must provide var environment variable. Exiting...."
exit 1
fi
常规检查将失败,如果未设置,则扩展为空字符串而不失败。[ -z "$var" ]
var; unbound variable
set -u
[ -z "${var-}" ]
var
评论
${var:-}
${var-}
${var:-}
和意思不同。 如果未设置或 null,则将扩展为空字符串,并且仅在未设置时将扩展为空字符串。${var-}
${var:-}
var
${var-}
如果未设置,则要退出
这对我有用。如果未设置参数,我希望我的脚本退出并显示错误消息。
#!/usr/bin/env bash
set -o errexit
# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"
这在运行时返回并显示错误
peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument
仅检查,无退出 - 空和未设置无效
如果您只想检查值 set=VALID 还是 unset/empty=INVALID,请尝试此选项。
TSET="good val"
TEMPTY=""
unset TUNSET
if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
或者,即使是简短的测试;-)
[ "${TSET:-}" ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"
仅检查,无退出 - 只有空是无效的
这就是问题的答案。 如果您只想检查值 set/empty=VALID 还是 unset=INVALID,请使用此选项。
注意,“..-1}“是微不足道的, 它可以是任何东西(如 X)
TSET="good val"
TEMPTY=""
unset TUNSET
if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
简短的测试
[ "${TSET+1}" ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"
我把这个答案献给@mklement0(评论),他们挑战我准确回答这个问题。
参考: 2.6.2 参数扩展
我总是发现另一个答案中的 POSIX 表很慢,所以这是我的看法:
参数扩展 | VARIABLE 设置 |
VARIABLE 空 |
VARIABLE 未凝固的 |
---|---|---|---|
${VARIABLE-default} |
$VARIABLE |
"" |
"default" |
${VARIABLE=default} |
$VARIABLE |
"" |
$(VARIABLE="default") |
${VARIABLE?default} |
$VARIABLE |
"" |
exit 127 |
${VARIABLE+default} |
"default" |
"default" |
"" |
${VARIABLE:-default} |
$VARIABLE |
"default" |
"default" |
${VARIABLE:=default} |
$VARIABLE |
$(VARIABLE="default") |
$(VARIABLE="default") |
${VARIABLE:?default} |
$VARIABLE |
exit 127 |
exit 127 |
${VARIABLE:+default} |
"default" |
"" |
"" |
请注意,每个组(带和不带前面的冒号)都具有相同的 set 和 unset 大小写,因此唯一不同的是如何处理空大小写。
对于前面的冒号,空大小写和未设置的大小写是相同的,所以我会尽可能使用它们(即使用 ,而不仅仅是 ,因为空大小写不一致)。:=
=
标题:
- set means 为非空 (
VARIABLE
VARIABLE="something"
) - empty 表示为空/null (
VARIABLE
VARIABLE=""
) - unset 表示不存在 (
VARIABLE
unset VARIABLE
)
值:
$VARIABLE
表示结果是变量的原始值。"default"
表示结果是提供的替换字符串。""
表示结果为 null(空字符串)。exit 127
表示脚本停止执行,退出代码为 127。$(VARIABLE="default")
表示结果是 ,并且(以前为空或未设置)也将设置为等于 。"default"
VARIABLE
"default"
评论
VARIABLE=""
VARIABLE=
VARIABLE
A=${VARIABLE=default}
A
"default"
VARIABLE
$(VARIABLE="default")
$(VARIABLE="default")
"default"
VARIABLE
"default"
在浏览了所有答案之后,这也有效:
if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"
如果你不放而不是我所拥有的,它会将其设置为空值; 这是这是必要的。SOME_VAR
$SOME_VAR
$
评论
set -u
unboud variable
总结
用于检查变量是否不为空(因此也必须定义/设置)。用法:
test -n "${var-}"
if test -n "${var-}"; then echo "var is set to <$var>" else echo "var is not set or empty" fi
用于检查变量是否已定义/设置(即使它为空)。用法:
test -n "${var+x}"
if test -n "${var+x}"; then echo "var is set to <$var>" else echo "var is not set" fi
请注意,第一个用例在 shell 脚本中更为常见,这是您通常想要使用的用例。
笔记
- 此解决方案应适用于所有 POSIX shell(sh、bash、zsh、ksh、dash)
- 这个问题的其他一些答案是正确的,但对于没有 shell 脚本经验的人来说可能会感到困惑,所以我想提供一个对这些人最不容易混淆的 TLDR 答案。
解释
要了解此解决方案的工作原理,您需要了解 POSIX 测试
命令和 POSIX shell 参数扩展 (spec),因此让我们介绍理解答案所需的绝对基础知识。
test 命令计算表达式并返回 true 或 false(通过其退出状态)。如果操作数为非空字符串,则运算符返回 true。例如,返回 true,而返回 false。现在,要检查变量是否不为空(这意味着必须定义它),您可以使用 .但是,某些 shell 脚本有一个选项集 (),该选项集会导致对未定义变量的任何引用发出错误,因此,如果未定义变量,表达式将导致错误。要正确处理这种情况,您必须使用变量扩展,如果未定义变量,它将告诉 shell 将变量替换为替代字符串,从而避免上述错误。-n
test -n "a"
test -n ""
test -n "$var"
set -u
var
$var
变量扩展意味着:如果变量未定义(也称为“unset”),请将其替换为空字符串。因此,如果不为空,将返回 true,这几乎总是您要在 shell 脚本中检查的内容。如果 undefined 或不为空,则反向检查将为 。${var-}
var
test -n "${var-}"
$var
$var
test -z "${var-}"
现在进入第二个用例:检查变量是否被定义,是否为空。这是一个不太常见的用例,稍微复杂一些,我建议你阅读 Lionels 的精彩答案以更好地理解它。var
评论
test -z
test -z "${var-}"
声明一个简单的函数,用于直接测试变量是否存在。is_set
declare -p
$ is_set() {
declare -p $1 >/dev/null 2>&1
}
$ is_set foo; echo $?
0
$ declare foo
$ is_set foo; echo $?
1
为了清楚地回答 OP 关于如何确定变量是否被设置的问题,Lionel 的答案是正确的:
if test "${name+x}"; then
echo 'name is set'
else
echo 'name is not set'
fi
这个问题已经有很多答案,但没有一个提供真正的布尔表达式来清楚地区分变量值。
以下是我制定的一些明确的表达方式:
+-----------------------+-------------+---------+------------+
| Expression in script | name='fish' | name='' | unset name |
+-----------------------+-------------+---------+------------+
| test "$name" | TRUE | f | f |
| test -n "$name" | TRUE | f | f |
| test ! -z "$name" | TRUE | f | f |
| test ! "${name-x}" | f | TRUE | f |
| test ! "${name+x}" | f | f | TRUE |
+-----------------------+-------------+---------+------------+
顺便说一句,这些表达式是等价的:test <expression>
<=> [ <expression> ]
其他模棱两可的表达方式应谨慎使用:
+----------------------+-------------+---------+------------+
| Expression in script | name='fish' | name='' | unset name |
+----------------------+-------------+---------+------------+
| test "${name+x}" | TRUE | TRUE | f |
| test "${name-x}" | TRUE | f | TRUE |
| test -z "$name" | f | TRUE | TRUE |
| test ! "$name" | f | TRUE | TRUE |
| test ! -n "$name" | f | TRUE | TRUE |
| test "$name" = '' | f | TRUE | TRUE |
+----------------------+-------------+---------+------------+
要测试是否设置了变量:。var
[ ${var+x} ]
要测试变量是否按名称设置,请执行以下操作。[ ${!name+x} ]
要测试是否设置了位置参数: ,其中 N 实际上是一个整数。[ ${N+x} ]
这个答案几乎与莱昂内尔的答案相似,但通过省略 .-z
要测试是否设置了命名变量,请执行以下操作:
function is_set {
local v=$1
echo -n "${v}"
if [ ${!v+x} ]; then
echo " = '${!v}'"
else
echo " is unset"
fi
}
要测试是否设置了位置参数:
function a {
if [ ${1+x} ]; then
local arg=$1
echo "a '${arg}'"
else
echo "a: arg is unset"
fi
}
测试表明,不需要格外注意空格和有效的测试表达式。
set -eu
V1=a
V2=
V4=-gt
V5="1 -gt 2"
V6="! -z 1"
V7='$(exit 1)'
is_set V1
is_set V2
is_set V3
is_set V4
is_set V5
is_set V6
is_set V7
a 1
a
a "1 -gt 2"
a 1 -gt 2
$ ./test.sh
V1 = 'a'
V2 = ''
V3 is unset
V4 = '-gt'
V5 = '1 -gt 2'
V6 = '! -z 1'
V7 = '$(exit 1)'
a '1'
a: arg is unset
a '1 -gt 2'
a '1'
最后,请注意 which 保护我们免受常见错误的影响,例如变量名称中的拼写错误。我建议使用它,但这意味着正确处理了未设置的变量和具有空字符串的变量集之间的差异。set -eu
我很惊讶没有人尝试编写一个 shell 脚本来以编程方式生成臭名昭著的难以摸索的表。既然我们在这里尝试学习编码技术,为什么不用代码表达答案呢?:)这是我的看法(应该适用于任何 POSIX shell):
H="+-%s-+-%s----+-%s----+-%s--+\n" # table divider printf format
R="| %-10s | %-10s | %-10s | %-10s |\n" # table row printf format
S='V' # S is a variable that is set-and-not-null
N='' # N is a variable that is set-but-null (empty "")
unset U # U is a variable that is unset
printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "expression" "FOO='V'" "FOO='' " "unset FOO";
printf "$H" "----------" "-------" "-------" "---------";
printf "$R" "\${FOO:-x}" "${S:-x}" "${N:-x}" "${U:-x} "; S='V';N='';unset U
printf "$R" "\${FOO-x} " "${S-x} " "${N-x} " "${U-x} "; S='V';N='';unset U
printf "$R" "\${FOO:=x}" "${S:=x}" "${N:=x}" "${U:=x} "; S='V';N='';unset U
printf "$R" "\${FOO=x} " "${S=x} " "${N=x} " "${U=x} "; S='V';N='';unset U
# "${N:?x}" "${U:?x} "
printf "$R" "\${FOO:?x}" "${S:?x}" "<error>" "<error> "; S='V';N='';unset U
# "${U?x} "
printf "$R" "\${FOO?x} " "${S?x} " "${N?x} " "<error> "; S='V';N='';unset U
printf "$R" "\${FOO:+x}" "${S:+x}" "${N:+x}" "${U:+x} "; S='V';N='';unset U
printf "$R" "\${FOO+x} " "${S+x} " "${N+x} " "${U+x} "; S='V';N='';unset U
printf "$H" "----------" "-------" "-------" "---------";
以及运行脚本的输出:
+------------+------------+------------+------------+
| expression | FOO='V' | FOO='' | unset FOO |
+------------+------------+------------+------------+
| ${FOO:-x} | V | x | x |
| ${FOO-x} | V | | x |
| ${FOO:=x} | V | x | x |
| ${FOO=x} | V | | x |
| ${FOO:?x} | V | <error> | <error> |
| ${FOO?x} | V | | <error> |
| ${FOO:+x} | x | | |
| ${FOO+x} | x | x | |
+------------+------------+------------+------------+
该脚本缺少一些功能,例如显示副作用分配何时发生(或不发生),但也许其他更雄心勃勃的人想要采取这个起点并使用它。
评论
如果你像我一样,你所寻找的实际上是
“如果设置了变量,bash 仅运行命令”
你希望它作为一个单行,所以下面的行就是你想要的
仅适用于 Bash 4.2 或更高版本
仅在设置时运行
if [[ -v mytest ]]; then echo "this runs only if variable is set"; fi
仅在未设置时运行
if [[ ! -v mytest2 ]]; then echo "this runs only if variable is not set"; fi
评论
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.-v
bash