如何检查一个变量是否在Bash中设置

如何知道一个变量是否在Bash中设置?

例如,如何检查用户是否为函数提供了第一个参数?

function a {# if $1 is set ?}
2010977 次浏览
if [ "$1" != "" ]; thenecho \$1 is setelseecho \$1 is not setfi

尽管对于参数,通常最好测试$#,这是参数的数量,在我看来。

if [ $# -gt 0 ]; thenecho \$1 is setelseecho \$1 is not setfi

有很多方法可以做到这一点,以下是其中之一:

if [ -z "$1" ]

如果1美元为空或未设置,则成功。

你可以这样做:

function a {if [ ! -z "$1" ]; thenecho '$1 is set'fi}

要检查非空/非零字符串变量,即如果设置了,请使用

if [ -n "$1" ]

这与-z相反。我发现自己使用-n比使用-z更多。

你会像这样使用它:

if [ -n "$1" ]; thenecho "You supplied the first parameter!"elseecho "First parameter not supplied."fi
case "$1" in"") echo "blank";;*) echo "set"esac

要检查变量是否设置为非空值,请使用[ -n "$x" ],正如其他人已经指出的那样。

大多数时候,将具有空值的变量与未设置的变量以相同的方式处理是一个好主意。但如果需要,您可以区分两者:[ -n "${x+set}" ](如果设置了x,则"${x+set}"扩展为set,如果设置了x,则扩展为空字符串)。

要检查参数是否已传递,请测试$#,这是传递给函数(或脚本,当不在函数中时)的参数数(请参阅保罗的回答)。

阅读bash手册页的“参数扩展”部分。参数扩展不提供对正在设置的变量的一般测试,但如果未设置参数,您可以对参数执行几件事。

例如:

function a {first_arg=${1-foo}# rest of the function}

如果赋值,则将first_arg设置为$1,否则使用值“foo”。如果a绝对必须采用单个参数,并且不存在良好的默认值,则可以在没有给出参数时显示错误消息退出:

function a {: ${1?a must take a single argument}# rest of the function}

(注意使用:作为空命令,它只是扩展其参数的值。在这个例子中,我们不想对$1做任何事情,如果没有设置,只需退出)

要检查变量是否已设置:

var=""; [[ $var ]] && echo "set" || echo "not 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))。

然而,虽然引号可以安全地省略,并且并非所有人都立即明显(甚至对这篇文章的第一作者引用了解释来说也不明显,他也是一个主要的Bash编码器),有时最好将引号编写为[ -z "${var+x}" ]的解决方案,代价可能很小O(1)速度惩罚。第一作者还将此作为注释添加到使用该解决方案的代码旁边,给出了该答案的URL,现在还包括解释为什么引号可以安全地省略。

(经常)错误的方式

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

这通常是错误的,因为它没有区分未设置的变量和设置为空字符串的变量。也就是说,如果var='',那么上述解决方案将输出“var为空”。

在用户必须指定扩展名或附加属性列表的情况下,unset和“设置为空字符串”之间的区别是必不可少的,并且不指定它们默认为非空值,而指定空字符串应该使脚本使用空扩展名或附加属性列表。

不过,这种区别可能并非在所有情况下都是必要的。在这些情况下,[ -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 allif [[ ! ${1+x} ]]; then ...   # We were called with no arguments

如果您希望测试变量是绑定还是未绑定,即使您打开了nounset选项,这也可以很好地工作:

set -o noun set
if printenv variableName >/dev/null; then# variable is bound to a valueelse# variable is unboundfi

以下是如何测试参数是取消设置空("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:参数扩展

在所有显示为“替换”的情况下,表达式都被替换为显示的值。在所有显示为“分配”的情况下,参数被分配该值,该值也替换了表达式。

在行动中展示这一点:

+--------------------+----------------------+-----------------+-----------------+|   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           | ""              |+--------------------+----------------------+-----------------+-----------------+
if [[ ${1:+isset} ]]then echo "It was set and not null." >&2else echo "It was not set or it was null." >&2fi
if [[ ${1+isset} ]]then echo "It was set but might be null." >&2else echo "It was was not set." >&2fi

虽然这里介绍的大多数技术都是正确的,但Bash 4.2支持对变量(manbash)的存在进行实际测试,而不是测试变量的值。

[[ -v foo ]]; echo $?# 1
foo=bar[[ -v foo ]]; echo $?# 0
foo=""[[ -v foo ]]; echo $?# 0

值得注意的是,这种方法在set -u/set -o nounset模式下用于检查未设置变量时不会导致错误,这与许多其他方法不同,例如使用[ -z

在现代版本的Bash上(我认为是4.2或更高版本;我不确定),我会尝试这样做:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigilthenecho "Variable is unset"elif [ -z "$SOMEVARIABLE" ]thenecho "Variable is set to an empty string"elseecho "Variable is set to some string"fi

我总是使用这个,因为它似乎很容易被第一次看到代码的人理解:

if [ "$variable" = "" ]thenecho "Variable X is empty"fi

而且,如果要检查是否不是空的;

if [ ! "$variable" = "" ]thenecho "Variable X is not empty"fi

就这样了。

当启用Bash选项set -u时,上面的答案不起作用。此外,它们不是动态的,例如,如何测试定义名称为“虚拟”的变量?试试这个:

is_var_defined(){if [ $# -ne 1 ]thenecho "Expected exactly one argument: variable name as string, e.g., 'my_var'"exit 1fi# 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”模式下定义

如果您想检查$@中的任何内容,我发现了一个(更好的)代码来执行此操作。

if [[ $1 = "" ]]thenecho '$1 is blank'elseecho '$1 is filled up'fi

为什么会这样?$@中的所有内容都存在于Bash中,但默认情况下它是空白的,所以test -ztest -n帮不了你。

更新时间:您还可以计算参数中的字符数。

if [ ${#1} = 0 ]thenecho '$1 is blank'elseecho '$1 is filled up'fi
[[ $foo ]]

(( ${#foo} ))

let ${#foo}

declare -p foo

在shell中,您可以使用-z运算符,如果字符串长度为零,则为True。

一个简单的单行设置默认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变量:

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

备注:

  • #0:为您提供位置参数的数量。
  • #0:为您提供作为参数传递的变量的定义。如果存在,则返回0,如果不存在,则返回1并打印错误消息。
  • #0:抑制declare -p的输出而不影响其返回代码。

我最喜欢的方式是这样的:

$ var=10$ if ! ${var+false};then echo "is set";else echo "NOT set";fiis set$ unset -v var$ if ! ${var+false};then echo "is set";else echo "NOT set";fiNOT set

所以基本上,如果一个变量被设置,它就变成了“结果false的否定”(将是true=“被设置”)。

而且,如果未设置,它将成为“结果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
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi

在Bash中,您可以在[[ ]]内置中使用-v

#! /bin/bash -u
if [[ ! -v SOMEVAR ]]; thenSOMEVAR='hello'fi
echo $SOMEVAR

说明

由于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,将常规的declare -p var0和stderr输出重定向到declare -p var2,永远看不见,并且不更改状态代码。因此该函数只返回状态代码。

为什么其他方法(有时)在Bash中失败

  • #0:这仅检查${var[0]}是否为非空。(在Bash中,$var${var[0]}相同。)
  • #0:这仅检查是否设置了${var[0]}
  • #0:这仅检查是否设置了$var的至少一个索引。

当此方法在Bash中失败时

这仅适用于命名变量(包括$_),不适用于某些特殊变量($!$@$#$$$*$?$-$0$1$!0,…,以及任何我可能忘记的)。由于这些都不是数组,POSIX风格的$!1适用于所有这些特殊变量。但要小心将其包装在函数中,因为许多特殊变量在调用函数时会更改值/存在。

外壳兼容性说明

如果你的脚本有数组,并且你想让它与尽可能多的shell兼容,那么考虑使用typeset -p而不是declare -p。我读到ksh只支持前者,但还没能测试一下。我确实知道Bash 3.0+和Zsh 5.5.1都支持typeset -pdeclare -p,区别只在于哪个是另一个的替代方案。但是除了这两个关键字之外,我还没有测试过其他的shell。

如果您需要脚本与POSIX sh兼容,那么您就不能使用数组。没有数组,[ -n "{$var+x}" ]有效。

Bash中不同方法的比较代码

此函数取消设置变量vareval是传递的代码,运行测试以确定var是否由evald代码设置,最后显示不同测试的结果状态代码。

我跳过了test -v var[ -v var ][[ -v var ]],因为它们产生的结果与POSIX标准[ -n "${var+x}" ]相同,同时需要Bash 4.2+。我还跳过了typeset -p,因为它与我测试过的shell中的declare -p相同(Bash 3.0到5.0,和Zsh 5.5.1)。

is-var-set-after() {# Set var by passed expression.unset vareval "$1"
# Run the tests, in increasing order of accuracy.[ -n "$var" ] # (index 0 of) var is nonemptynonempty=$?[ -n "${var+x}" ] # (index 0 of) var is set, maybe emptyplus=$?[ "${#var[@]}" != 0 ] # var has at least one index set, maybe emptycount=$?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  0is-var-set-after "var=(foo)"                      #  0  0  0  0is-var-set-after "var=([0]=foo)"                  #  0  0  0  0is-var-set-after "var=([x]=foo)"                  #  0  0  0  0is-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  0is-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  0is-var-set-after "var=([1]=foo)"                  #  1  1  0  0is-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  0is-var-set-after "declare -a var"                 #  1  1  1  0is-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 #@ -pvar=foo:  0  0  0  0var=(foo):  0  0  0  0var=([0]=foo):  0  0  0  0var=([x]=foo):  0  0  0  0var=([y]=bar [x]=foo):  0  0  0  0var='':  1  0  0  0var=([0]=''):  1  0  0  0var=([1]=''):  1  1  0  0var=([1]=foo):  1  1  0  0declare -A var; var=([x]=foo):  1  1  0  0var=():  1  1  1  0declare -a var:  1  1  1  0declare -A var:  1  1  1  0unset var:  1  1  1  1

总结

  • #0(100%?)至少从3.0开始在Bash中测试命名变量是可靠的。
  • #0在符合POSIX的情况下是可靠的,但不能处理数组。
  • 还有其他测试用于检查变量是否为非空,以及检查其他shell中声明的变量。但这些测试既不适合Bash也不适合POSIX脚本。

我喜欢用辅助函数来隐藏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} ]}

因为我第一次在这个实现中遇到了错误(受和莱昂内尔6753536">Jens的回答和莱昂内尔的启发),我想出了一个不同的解决方案:

# Ask for the properties of the variable - fails if not declaredfunction IsDeclared(){declare -p $1 &>/dev/null}

我觉得它更直接,更害羞,更容易理解/记住。测试用例显示它是等价的:

function main(){declare -i xyzlocal foolocal 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

测试用例还显示local var确实没有声明了var(除非后面跟着 '='). 很长一段时间我以为我是这样声明变量的,现在才发现我只是表达了我的意图……我想这是一个无操作。

IsDeclared_Trickyxyz: 1IsDeclared_Trickyfoo: 1IsDeclared_Tricky:0IsDeclared_Trickybaz: 0IsDeclared xyz: 1已声明foo: 1IsDeclared bar: 0IsDeclared baz: 0

奖励: usecase

我主要使用这个测试以有点“优雅”和安全的方式(几乎类似于接口…)为函数提供(和返回)参数:

# Auxiliary functionsfunction die(){echo "Error: $1"; exit 1}
function assertVariableDeclared(){IsDeclared "$1" || die "variable not declared: $1"}
function expectVariables(){while (( $# > 0 )); doassertVariableDeclared $1; shiftdone}
# Actual examplefunction exampleFunction(){expectVariables inputStr outputStroutputStr="$inputStr, World!"}
function bonus(){local inputStr='Hello'local outputStr= # Remove this to trigger the errorexampleFunctionecho $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;}}
  • 通过首先测试变量是否设置(un),如果没有必要,可以避免调用声明。
  • 但是,如果$1包含一个空的$array=()的名称,则调用声明将确保我们得到正确的结果
  • 从来没有多少数据传递给 /dev/null因为只有在变量未设置或空数组时才会调用声明。

此函数将在以下条件下进行测试:

a;       # is not declareda=;      # is declareda="foo"; # is declareda=();    # is declareda=("");  # is declaredunset a; # is not declared
a;       # is unseta=;      # is not unseta="foo"; # is not unseta=();    # is not unseta=("");  # is not unsetunset a; # is unset

.

了解更多详情

和一个测试脚本,请参阅我的回答到问题“如何检查bash中是否存在变量?”

备注:declare -p的类似用法,正如Peregring-lk回答所示,确实是巧合。否则我当然会相信它!

对于那些希望在带有#0的脚本中检查未设置或空的人:

if [ -z "${var-}" ]; thenecho "Must provide var environment variable. Exiting...."exit 1fi

如果set -u,常规的[ -z "$var" ]检查将以var; unbound variable失败,但如果var未设置且未失败,则[ -z "${var-}" ]扩大为空字符串。

如果没设置好你想退出

这对我有用。如果未设置参数,我希望我的脚本退出并显示错误消息。

#!/usr/bin/env bash
set -o errexit
# Get the value and empty validation check all in oneVER="${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/空=INVALID,请尝试此选项。

TSET="good val"TEMPTY=""unset TUNSET
if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi# VALIDif [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi# INVALIDif [ "${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/空=VALID或unset=INVALID,请使用此选项。

注意,“…-1}”中的“1”是无关紧要的,它可以是任何东西(如x)

TSET="good val"TEMPTY=""unset TUNSET
if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi# VALIDif [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi# VALIDif [ "${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"

我把这个答案献给@mclement0(评论),他挑战我准确回答这个问题。

参考:参数扩展2.6.2参数扩展

我总是发现其他答案中的POSIX表很慢,所以这是我的看法:

参数展开VARIABLE setVARIABLEVARIABLE取消设置
${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}$VARIABLEexit 127exit 127
${VARIABLE:+default}"default"""""

请注意,每个组(有和没有前面的冒号)都有相同的取消设置案例,所以唯一不同的是如何处理案例。

对于前面的冒号,取消设置的情况是相同的,所以我会在可能的情况下使用它们(即使用:=,而不仅仅是=,因为空的情况不一致)。

标题:

  • 表示VARIABLE非空(VARIABLE="something"
  • 表示VARIABLE为空/null(VARIABLE=""
  • 取消设置表示VARIABLE不存在(unset VARIABLE

价值观:

  • $VARIABLE表示结果是变量的原始值。
  • "default"表示结果是提供的替换字符串。
  • ""表示结果为空(空字符串)。
  • exit 127表示脚本停止执行,退出代码为127。
  • $(VARIABLE="default")表示结果为"default"VARIABLE(以前为空或未设置)也将设置为等于"default"

在浏览了所有答案之后,这也有效:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fiecho "SOME_VAR=$SOME_VAR"

如果你不把SOME_VAR而不是我有$SOME_VAR,它会将其设置为空值;$是必要的。

总结

  • 使用test -n "${var-}"检查变量是否为空(因此也必须定义/设置)。用法:

    if test -n "${var-}"; thenecho "var is set to <$var>"elseecho "var is not set or empty"fi
  • 使用test -n "${var+x}"检查变量是否已定义/设置(即使它为空)。用法:

    if test -n "${var+x}"; thenecho "var is set to <$var>"elseecho "var is not set"fi

请注意,第一个用例在shell脚本中更为常见,这就是您通常想要使用的。

备注

  • 此解决方案应该适用于所有POSIX shell(sh、bash、zsh、ksh、dash)
  • 这个问题的其他一些答案是正确的,但对于没有shell脚本经验的人来说可能会感到困惑,所以我想提供一个TLDR答案,让这些人最不困惑。

补充说明

要了解此解决方案的工作原理,您需要了解POSIX#0命令和POSIX shell参数扩展(规格),因此让我们介绍理解答案所需的绝对基础知识。

test命令评估表达式并返回true或false(通过其退出状态)。如果操作数是非空字符串,运算符-n返回true。例如,test -n "a"返回true,而test -n ""返回false。现在,要检查变量是否为空(这意味着它必须被定义),您可以使用test -n "$var"。然而,一些shell脚本有一个选项集(set -u),它会导致任何对未定义变量的引用发出错误,因此如果变量var未定义,表达式$var将导致错误。要正确处理这种情况,您必须使用变量扩展,如果未定义,它将告诉shell用替代字符串替换变量,从而避免上述错误。

变量扩展${var-}意味着:如果变量var未定义(也称为“unset”),则用空字符串替换它。因此,如果$var不为空,test -n "${var-}"将返回true,这几乎总是您想要在shell脚本中检查的。反向检查,如果$var未定义或不为空,则为test -z "${var-}"

现在第二个用例:检查变量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关于如何确定变量是否设置的问题,莱昂内尔的回答是正确的:

if test "${name+x}"; thenecho 'name is set'elseecho '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+x} ],其中N实际上是一个整数。

这个答案几乎与莱昂内尔的答案相似,但通过省略-z来探索更简约的方法。

要测试是否设置了命名变量:

function is_set {local v=$1echo -n "${v}"if [ ${!v+x} ]; thenecho " = '${!v}'"elseecho " is unset"fi}

要测试是否设置了位置参数:

function a {if [ ${1+x} ]; thenlocal arg=$1echo "a '${arg}'"elseecho "a: arg is unset"fi}

测试表明,不需要额外注意空格和有效的测试表达式。

set -eu
V1=aV2=V4=-gtV5="1 -gt 2"V6="! -z 1"V7='$(exit 1)'
is_set V1is_set V2is_set V3is_set V4is_set V5is_set V6is_set V7
a 1aa "1 -gt 2"a 1 -gt 2
$ ./test.sh
V1 = 'a'V2 = ''V3 is unsetV4 = '-gt'V5 = '1 -gt 2'V6 = '! -z 1'V7 = '$(exit 1)'a '1'a: arg is unseta '1 -gt 2'a '1'

最后,请注意set -eu,它可以保护我们免受常见错误的影响,例如变量名称中的拼写错误。我推荐它的使用,但这意味着未设置变量和使用空字符串设置的变量之间的差异得到了正确处理。

我很惊讶没有人尝试编写外壳脚本来以编程方式生成那张臭名昭著的难搞的桌子。既然我们在这里尝试学习编码技术,为什么不用代码来表达答案呢?:)这是我的看法(应该适用于任何POSIX shell):

H="+-%s-+-%s----+-%s----+-%s--+\n"       # table divider printf formatR="| %-10s | %-10s | %-10s | %-10s |\n"  # table row printf format
S='V'     # S is a variable that is set-and-not-nullN=''      # 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 Uprintf "$R" "\${FOO-x} " "${S-x} " "${N-x} " "${U-x}   "; S='V';N='';unset Uprintf "$R" "\${FOO:=x}" "${S:=x}" "${N:=x}" "${U:=x}  "; S='V';N='';unset Uprintf "$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 Uprintf "$R" "\${FOO:+x}" "${S:+x}" "${N:+x}" "${U:+x}  "; S='V';N='';unset Uprintf "$R" "\${FOO+x} " "${S+x} " "${N+x} " "${U+x}   "; S='V';N='';unset Uprintf "$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