如果我想检查空字符串,我会做
[ -z $mystr ]
但是如果我想检查变量是否已经被定义呢?或者 Bash 脚本没有区别?
我认为Vinko的答案暗示了您所追求的答案(如果没有说明),尽管它没有简单地拼写出来。要区分 VAR 是否已设置但为空或未设置,您可以使用:
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
您可能可以将第二行的两个测试合并为一个:
if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
但是,如果您阅读 Autoconf 的文档,您会发现他们不建议将术语与 ' -a
' 结合使用,而是建议使用单独的简单测试与&&
. 我没有遇到有问题的系统;这并不意味着它们过去不存在(但它们现在可能非常罕见,即使它们在遥远的过去并不罕见)。
您可以在 Bash 手册中找到这些以及其他相关的shell 参数扩展、test
或[
命令和条件表达式的详细信息。
最近有人通过电子邮件向我询问了这个问题的答案:
您使用了两个测试,我很了解第二个,但不是第一个。更准确地说,我不明白变量扩展的必要性
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
这不也能达到同样的效果吗?
if [ -z "${VAR}" ]; then echo "VAR is not set at all"; fi
公平的问题 - 答案是“不,您更简单的替代方案不会做同样的事情”。
假设我在你的测试之前写了这个:
VAR=
您的测试会说“根本没有设置 VAR”,但我会说(暗示,因为它什么都没有回显)“设置了 VAR,但它的值可能为空”。试试这个脚本:
(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo "JL:1 VAR is not set at all"; fi
if [ -z "${VAR}" ]; then echo "MP:1 VAR is not set at all"; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo "JL:2 VAR is not set at all"; fi
if [ -z "${VAR}" ]; then echo "MP:2 VAR is not set at all"; fi
)
输出是:
JL:1 VAR 根本没有设置 MP:1 VAR 根本没有设置 MP:2 VAR 根本没有设置
在第二对测试中,变量被设置,但它被设置为空值。这就是 the${VAR=value}
和${VAR:=value}
符号的区别。和、 和${VAR-value}
、等等也是如此。${VAR:-value}
${VAR+value}
${VAR:+value}
正如Gili在他的回答中指出的那样,如果您bash
使用该set -o nounset
选项运行,那么上面的基本答案会以unbound variable
. 它很容易补救:
if [ -z "${VAR+xxx}" ]; then echo "VAR is not set at all"; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo "VAR is set but empty"; fi
或者您可以使用(等价于)取消该set -o nounset
选项。set +u
set -u
set -o nounset
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~>
-z 也适用于未定义的变量。要区分未定义和已定义,您将使用此处列出的内容,或者使用更清晰的解释,请使用此处。
最干净的方法是使用这些示例中的扩展。要获得所有选项,请查看手册的参数扩展部分。
替代词:
~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED
默认值:
~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED
当然,您会以不同的方式使用其中之一,放置您想要的值而不是“默认值”,并在适当的情况下直接使用扩展。
高级 bash 脚本指南,10.2。参数替换:
要将您的程序逻辑基于变量 $mystr 是否已定义,您可以执行以下操作:
isdefined=0
${mystr+ export isdefined=1}
现在,如果 isdefined=0 则变量未定义,如果 isdefined=1 变量已定义
这种检查变量的方法比上面的答案更好,因为它更优雅、更易读,并且如果您的 bash shell 配置为在使用未定义变量时出错(set -u)
,脚本将提前终止。
其他有用的东西:
如果 $mystr 未定义,则为其分配默认值 7,否则保持不变:
mystr=${mystr- 7}
如果变量未定义,则打印错误消息并退出函数:
: ${mystr? not defined}
请注意,我在这里使用了 ':',以免 $mystr 的内容作为命令执行,以防万一它被定义。
测试总结。
[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set" # may or may not be empty
[ -n "${var+x}" ] && echo "var is set" # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"
检查正在定义的变量的显式方法是:
[ -v mystr ]
https://stackoverflow.com/a/9824943/14731包含一个更好的答案(一个更具可读性并且可以set -o nounset
启用)。它的工作原理大致如下:
if [ -n "${VAR-}" ]; then
echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
echo "VAR is set, but empty"
else
echo "VAR is not set"
fi
另一种选择:“列表数组索引”扩展:
$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1
唯一一次扩展为空字符串是 whenfoo
未设置,因此您可以使用字符串条件检查它:
$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0
应该在任何 bash 版本 >= 3.0 中可用
不要进一步摆脱这辆自行车,但想添加
shopt -s -o nounset
是您可以添加到脚本顶部的内容,如果未在脚本中的任何位置声明变量,则会出错。您会看到的消息是unbound variable
,但正如其他人提到的那样,它不会捕获空字符串或空值。为了确保任何单个值都不为空,我们可以测试一个变量,因为它使用 扩展${mystr:?}
,也称为美元符号扩展,这会与parameter null or not set
.
Bash 参考手册是有关 bash 的权威信息来源。
这是一个测试变量以查看它是否存在的示例:
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
(来自第 6.3.2 节。)
请注意,在 open 之后[
和之前的空格]
不是可选的。
Vim 用户提示
我有一个脚本,其中有几个声明如下:
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
但我希望他们尊重任何现有的价值观。所以我把它们改写成这样:
if [ -z "$VARIABLE_NAME" ]; then
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi
我能够使用快速正则表达式在 vim 中自动执行此操作:
s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r export \1=\2\rfi\r/gc
这可以通过在视觉上选择相关行,然后键入来应用:
。命令栏预填充:'<,'>
. 粘贴上面的命令并回车。
在这个版本的 Vim 上测试:
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com
Windows 用户可能需要不同的行尾。
这是我认为检查变量是否已定义的更清晰的方法:
var_defined() {
local var_name=$1
set | grep "^${var_name}=" 1>/dev/null
return $?
}
按如下方式使用它:
if var_defined foo; then
echo "foo is defined"
else
echo "foo is not defined"
fi
测试未定义变量的较短版本可以简单地是:
test -z ${mystr} && echo "mystr is not defined"
不带任何参数的调用 set .. 它输出所有定义的变量 ..
列表中的最后一个将是脚本中定义的变量 ..
所以你可以将它的输出通过管道传递给可以确定定义了什么和没有定义什么的东西