如何判断一个字符串是否在 BashShell 脚本中定义

如果我想检查空字符串,我会这样做

[ -z $mystr ]

但是如果我想检查变量是否已经被定义了呢?还是说在 Bash 脚本中没有区别?

188788 次浏览
~> 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

当然,您可以以不同的方式使用其中的一个,将您想要的值放在一起,而不是“默认值”,并在适当的时候直接使用展开式。

不带任何参数调用 一个 href = “ https://ss64.com/bash/set.html”rel = “ nofollow norefrer”> set ... 它输出所有定义的变量。

列表中的最后一个应该是脚本中定义的那些。

所以你可以通过管道将它的输出传递给某个东西,这样就可以知道什么东西被定义了,什么东西没有被定义。

我认为你所追求的答案是由 文科回答所暗示的(如果没有陈述的话) ,虽然它不是简单地说明。要区分 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 手册中找到这些以及其他相关 外壳参数扩展ABC0或 [命令和 条件表达式的详细信息。


最近有人通过电子邮件问我这个问题的答案:

你使用两个测试,我很了解第二个,但不是第一个。更确切地说,我不明白变量扩展的必要性

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},等等也是如此。


正如 吉莉在他的 回答中指出的,如果您使用 set -o nounset选项运行 bash,那么上面的基本答案对于 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 +uset -o nounset选项(set -u等效于 set -o nounset)。

高级 Bash 脚本指南,10.2。参数替换:

  • ${ var + blahbrah } : 如果定义了 var,则“ blahbrah”将替换 表达式,则用 else null 替换
  • ${ var-blahbra } : 如果定义了 var,那么它本身就是替换的,否则 “吧啦吧啦”被取代了
  • ${ var? blahbra } : 如果定义了 var,它将被替换,否则 函数中存在一个错误消息“ blahba”。

要根据是否定义了变量 $mystr 来编写程序逻辑,可以执行以下操作:

isdefined=0
${mystr+ export isdefined=1}

现在,如果被定义为 = 0,那么变量就是未定义的,如果被定义为 = 1,那么变量就被定义了。

这种检查变量的方法比以前的答案更好,因为它更优雅、更易读,而且如果您的 Bash shell 配置为在使用未定义变量 (set -u)时出错,脚本将提前终止。

其他有用的东西:

如果 $mystr 未定义,则默认值为7,否则保持不变:

mystr=${mystr- 7}

如果变量未定义,则打印错误消息并退出函数:

: ${mystr? not defined}

在这里要注意,我使用了’:’,以免在定义 $mystr 的情况下将 $mystr 的内容作为命令执行。

下面是我认为更清晰的检查变量是否定义的方法:

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

另一种选择是 “列表数组索引”扩展:

$ unset foo
$ foo=
$ echo ${!foo[*]}
0


$ foo=bar
$ echo ${!foo[*]}
0


$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

只有当 foo未设置时才会扩展为空字符串,因此您可以使用字符串条件检查它:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1


$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0


$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0


$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

应该在任何 Bash 版本3.0或更高版本中提供。

测试总结。

[ -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"

没有到 扔掉这辆自行车更进一步,但想补充

shopt -s -o nounset

是可以添加到脚本顶部的东西,如果脚本中的任何地方的变量不是 声明,那么将出错。

您将看到的消息是 未绑定变量未绑定变量,但正如其他人提到的,它不会捕获空字符串或空值。

为了确保任何单独的值都不是空的,我们可以测试一个用 ${mystr:?}展开的变量,也称为美元符号展开,在 parameter null or not set中会出错。

检查正在定义的变量的显式方法是:

[ -v mystr ]

当使用“ set-o nounset” 包含一个更好的答案(一个可读性更好且启用 set -o nounset的答案)时,测试 bash 中是否设置了一个变量。它的工作原理大致如下:

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

Bash 参考手册是关于 Bash 的权威信息来源。

下面是一个测试变量是否存在的例子:

if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi

(来自 第6.3.2条)

请注意,打开 [之后和 ]之前的空格是 没得选择


维姆用户的提示

我有一个脚本,有几个声明如下:

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 用户可能需要不同的行尾。

测试未定义变量的简短版本可以简单地如下:

test -z ${mystr} && echo "mystr is not defined"