如果我想检查空字符串,我会做
[ -z $mystr ]
但是,如果我想检查变量是否已经定义,该怎么办?还是Bash脚本没有区别?
[ -n "${VAR+x}"] && echo not null
[ -z ]
代替[ -z "" ]
; 如果有空间,就成了[ -z "my" "test" ]
代替[ -z my test ]
; 如果为[ -z * ]
,则将*
替换为目录中文件的名称。
如果我想检查空字符串,我会做
[ -z $mystr ]
但是,如果我想检查变量是否已经定义,该怎么办?还是Bash脚本没有区别?
[ -n "${VAR+x}"] && echo not null
[ -z ]
代替[ -z "" ]
; 如果有空间,就成了[ -z "my" "test" ]
代替[ -z my test ]
; 如果为[ -z * ]
,则将*
替换为目录中文件的名称。
Answers:
我认为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 is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all
在第二对测试中,设置了变量,但将其设置为空值。这是${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
)取消该选项。
${+}
并且${-}
还不清楚的程序员来说,但是要成为合格的Shell用户,熟悉这些结构是必不可少的。
set -o nounset
。
[ -v VAR ]
使用bash -s v4.2及更高版本来研究 “ ”方法。
~> 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)
,则脚本将过早终止。
其他有用的东西:
如果未定义,则将默认值7分配给$ mystr,否则保持不变:
mystr=${mystr- 7}
如果未定义变量,则输出错误消息并退出函数:
: ${mystr? not defined}
请注意,在这里我使用了':',以免$ mystr的内容在定义时作为命令执行。
var= ; varname=var ; : ${!varname?not defined}
并终止于:varname=var ; : ${!varname?not defined}
。但这是一个好习惯,使用set -u
起来容易得多。
测试摘要。
[ -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"
${var+x}
没有必要,除非允许变量名称中包含空格。
[
得到正确的结果。如果var
未设置或为空,[ -n $var ]
减小到[ -n ]
其中有退出状态0,而[ -n "$var" ]
具有为1的预期的(正确)的退出状态
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
检查定义的变量的显式方法是:
[ -v mystr ]
test
在Bourne Shell Builtins部分的结尾处由运算符引用。我不知道何时添加它,但是它不是bash
基于(基于bash
3.2.51的)Apple版本,因此它可能是4.x功能。
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
。错误是-bash: [: -v: unary operator expected
。根据此处的评论,它至少需要bash 4.2才能运行。
另一个选择:“列表数组索引”扩展:
$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1
唯一扩展为空字符串的时间foo
是未设置when ,因此您可以使用条件字符串检查它:
$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0
应该在任何大于等于3.0的bash版本中可用
该猛砸参考手册是有关bash的权威信息来源。
这是测试变量以查看其是否存在的示例:
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
(摘自第6.3.2节。)
需要注意的是后开的空白[
和以前]
是不可选的。
给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
可以通过视觉选择相关行,然后键入来应用:
。命令栏将以填充:'<,'>
。粘贴以上命令,然后按Enter。
在此版本的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
grep
很糟糕。请注意,bash
support ${!var_name}
作为一种获取名称在中指定的变量的值的方式$var_name
,所以name=value; value=1; echo ${name} ${!name}
yields value 1
。