如何检查一个variables是否在Bash中设置?
我如何知道是否在Bash中设置了一个variables?
例如,如何检查用户是否给函数第一个参数?
function a { # if $1 is set ? }
正确的方式
if [ -z ${var+x} ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
其中${var+x}
是一个参数扩展 ,如果var
未设置,那么它的计算结果为空,否则将其replace为stringx
。
行情数字
引号可以省略(所以我们可以说${var+x}
而不是"${var+x}"
),因为这个语法和用法保证这只会扩展到不需要引号的东西(因为它可以扩展为x
(它不包含任何分词,所以不需要引号),或者什么也不做(这导致了[ -z ]
,它便于计算出与[ -z "" ]
相同的值(true))。
然而,虽然引号可以被安全地省略,但并不是很明显( 对于这个引用解释的第一作者来说,谁也是一个主要的Bash编码器甚至不是很明显),有时候最好写一个解决scheme与报价为[ -z "${var+x}" ]
,在一个O(1)速度惩罚的可能成本非常小。 第一位作者还在代码旁边添加了这个注释,使用这个解决scheme给出了这个答案的URL,现在还包括解释为什么可以安全地省略引号。
错误的方法
if [ -z "$var" ]; then echo "var is unset"; else echo "var is set to '$var'"; fi
这是因为它不区分未设置的variables和设置为空string的variables。 也就是说,如果var=''
,那么上面的解决scheme将错误地输出该var未设置。
但是这种区分在用户必须指定扩展名或附加属性列表的情况下是非常重要的,而不是指定它们默认为非空值,而指定空string应该使脚本使用空的扩展名或列表的附加属性。
检查非空/非零stringvariables,即如果设置,使用
if [ -n "$1" ]
这与-z
相反。 我发现自己使用-n
多于-z
。
以下是如何testing参数是未设置还是为空(“空”)或设置值 :
+--------------------+----------------------+-----------------+-----------------+ | | parameter | parameter | parameter | | | 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式都被replace为显示的值。 在所有用“分配”显示的情况下,参数都被分配了该值,该值也replace了expression式。
有很多方法可以做到这一点,以下是其中之一:
if [ -z "$1" ]
如果$ 1为空或未设置,则成功
虽然这里陈述的大部分技术都是正确的,但bash 4.2支持对variables( man bash )的存在进行实际testing,而不是testingvariables的值。
[[ -v foo ]]; echo $? # 1 foo=bar [[ -v foo ]]; echo $? # 0 foo="" [[ -v foo ]]; echo $? # 0
要看一个variables是不是空的,我使用
if [[ $var ]]; then ... # `$var' expands to a nonempty string
相反的testing,如果一个variables是未设置或空的:
if [[ ! $var ]]; then ... # `$var' expands to the empty string (set or not)
要查看是否设置了variables(空或非空),我使用
if [[ ${var+x} ]]; then ... # `var' exists (empty or nonempty) if [[ ${1+x} ]]; then ... # Parameter 1 exists (empty or nonempty)
相反的testing,如果一个variables未设置:
if [[ ! ${var+x} ]]; then ... # `var' is not set at all if [[ ! ${1+x} ]]; then ... # We were called with no arguments
在现代版本的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
if [ "$1" != "" ]; then echo \$1 is set else echo \$1 is not set fi
尽pipe对于参数来说,我认为通常最好testing$#,这是参数的数量。
if [ $# -gt 0 ]; then echo \$1 is set else echo \$1 is not set fi
阅读bash
手册页的“Parameter Expansion”部分。 参数扩展不会为所设置的variables提供一般testing,但是如果未设置参数,则可以对参数执行几个操作。
例如:
function a { first_arg=${1-foo} # rest of the function }
将first_arg
设置first_arg
等于$1
如果它被分配,否则它使用值“foo”。 如果绝对必须采用单个参数,并且不存在良好的默认值,则可以在不给出任何参数时退出并显示错误消息:
function a { : ${1?a must take a single argument} # rest of the function }
(注意:
使用:
作为一个空指令,它扩展了它的参数的值,在这个例子中我们不想做任何事情,只要没有设置就退出)
要检查variables是否设置为非空值,请使用[ -n "$x" ]
,如其他人已经指出的那样。
在大多数情况下,最好是像处理一个未设置的variables一样处理一个具有空值的variables。 但是,如果需要: [ -n "${x+set}" ]
( "${x+set}"
扩展为set
x
设置,如果x
未设置则返回空string)。
要检查一个参数是否已经通过,testing$#
,这是传递给函数的参数的数量(或者当不在函数中的时候,就是脚本)(参见Paul的答案 )。
我总是发现另一个答案中的POSIX表格慢了点,所以这是我的承诺:
+----------------------+------------+-----------------------+-----------------------+ | if VARIABLE is: | set | empty | unset | +----------------------+------------+-----------------------+-----------------------+ - | ${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" | "" | "" | +----------------------+------------+-----------------------+-----------------------+
请注意,每个组(具有和不具有前面的冒号)具有相同的设置和未设置的情况,所以唯一不同的是处理空白的情况。
对于前面的冒号, 空的和未设置的情况是相同的,所以我会尽可能使用那些(即使用:=
,不只是=
,因为空的情况是不一致的)。
标题:
- 设置意味着
VARIABLE
是非空的(VARIABLE="something"
) - 空表示
VARIABLE
为空/空(VARIABLE=""
) - 未设置表示
VARIABLE
不存在(未unset VARIABLE
)
价值观:
-
$VARIABLE
表示结果是variables的原始值。 -
"default"
表示结果是提供的replacestring。 -
""
表示结果为空(空string)。 -
exit 127
意味着脚本停止执行退出代码127。 -
$(VARIABLE="default")
表示结果是variables的原始值, 并且提供的replacestring被分配给该variables以备将来使用。
在bash中,你可以在[[ ]]
内部使用-v
:
#! /bin/bash -u if [[ ! -v SOMEVAR ]]; then SOMEVAR='hello' fi echo $SOMEVAR
如果未设置,您想退出
这对我有效。 如果没有设置参数,我希望我的脚本退出并显示错误消息。
#!/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
或者,即使是短暂的testing;-)
[ "${TSET:-}" ] && echo "VALID" || echo "INVALID" [ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID" [ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"
只检查,不退出 – 只有空是无效的
这就是问题的答案。 如果你只是想检查set / empty = VALID或unset = INVALID,那么使用这个。
注意,“..- 1}”中的“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
简短的testing
[ "${TSET+1}" ] && echo "VALID" || echo "INVALID" [ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID" [ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"
我把这个答案奉献给@ mklement0(评论),他挑战我准确地回答这个问题。
参考http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02
你可以做:
function a { if [ ! -z "$1" ]; then echo '$1 is set' fi }
当Bash选项set -u
被启用时,上面的答案不起作用。 另外,它们不是dynamic的,例如,如何testingvariables是否具有名称“dummy”被定义? 尝试这个:
is_var_defined() { if [ $# -ne 1 ] then echo "Expected exactly one argument: variable name as string, eg, '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中,如何testingvariables是否在“-u”模式下定义
使用[[ -z "$var" ]]
是知道variables是否被设置的最简单的方法,但是这个选项不区分unsetvariables和设置为空string的variables:
set='' $ [[ -z "$set" ]] && echo "Set" || echo "Unset" Unset $ [[ -z "$unset" ]] && echo "Set" || echo "Unset" Unset
最好根据variablestypes来检查它:envvariables,参数或常规variables。
对于一个envvariables:
[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"
对于参数(例如,检查是否存在参数“$ 5”:
[[ $# -ge 5 ]] && echo "Set" || echo "Unset"
对于常规variables(使用辅助函数,以一种优雅的方式):
function declare_var { declare -p "$1" &> /dev/null return $? } declare_var "var_name" && echo "Set" || echo "Unset"
笔记:
$# says you the number of positional parameters. declare -p gives you the definition of the variable passed as parameter. If it exists, returns 0, if not, returns 1 and prints an error message. $? gives you the status code of the last executed command.
在shell中,可以使用-z
运算符,如果string的长度为零,则为True。
一个简单的MY_VAR
设置默认的MY_VAR
如果没有设置,否则可以select显示消息:
[[ -z "$MY_VAR" ]] && MY_VAR="default" [[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
我最喜欢的方式是这样的:
$var=10 $if ! ${var+false};then echo "is set";else echo "NOT set";fi is set $unset var $if ! ${var+false};then echo "is set";else echo "NOT set";fi NOT set
所以基本上,如果一个variables被设置,它就变成了“否定所产生的false
”(将被设置为true
)。
而且,如果它未被设置,它将变成“否定所得到的true
”(因为空的结果评价为true
)(因此将结束为false
=“未设置”)。
对于那些正在寻找未设置或空的时候在脚本中set -u
:
if [ -z "${var-}" ]; then echo "Must provide var environment variable. Exiting...." exit 1 fi
常规的[ -z "$var" ]
检查将失败, var; unbound variable
如果set -u
则为非var; unbound variable
,但如果var
var; unbound variable
set -u
,则[ -z "${var-}" ]
展开为空string。
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 =“”]] 然后 回声'$ 1是空白' 其他 回声'$ 1已满' 科幻
为什么这一切? 在$@
所有内容都存在于Bash中,但是默认情况下它是空白的,所以test -z
和test -n
不能帮助你。
更新:您也可以统计参数中的字符数。
如果[$ {#1} = 0] 然后 回声'$ 1是空白' 其他 回声'$ 1已满' 科幻
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi
我总是使用这个,基于这样一个事实:任何人第一次看到代码似乎很容易理解:
if [ "$variable" = "" ] then echo "Variable X is empty" fi
而且,如果想检查是不是空的;
if [ ! "$variable" = "" ] then echo "Variable X is not empty" fi
而已。
这是我每天使用的:
# # Check if a variable is set # param1 name of the variable # function is_set() { [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")" }
这在Linux和Solaris下运行良好,至3.0。
bash-3.00$ myvar="TEST" bash-3.00$ is_set myvar ; echo $? 0 bash-3.00$ mavar="" bash-3.00$ is_set myvar ; echo $? 0 bash-3.00$ unset myvar bash-3.00$ is_set myvar ; echo $? 1
如果var
可以是一个数组,那么[ -z "${var+x}" ]
参数replace是不正确的。 要确定在Bash中,您需要使用[ "${#var[@]}" = 0 ]
这样的数组语法 ,如下所示。
is-var-set () { results="\${var+x}=${var+x}\t\${#var[@]}=${#var[@]}" if [ -z "${var+x}" ] && [ "${#var[@]}" = 0 ]; then echo -e "$1: var's unset.\t$results" elif [ -n "${var+x}" ] && [ "${#var[@]}" != 0 ]; then echo -e "$1: var is set. \t$results" else echo -e "$1: Is var set? \t$results" fi unset var # so we don't have to do it everywhere else }
几乎在所有情况下,他们都同意。 我发现数组方法更精确的唯一情况是variables是位置为0
的非空数组(例如,在下面的testing7
和A
中)。 这个不一致来自$var
,简写为${var[0]}
,所以[ -z "${var+x}" ]
不检查整个数组。
这是我的testing用例。
unset var; is-var-set 1 # var unset var=''; is-var-set 2 # var[0] set to '' var=foo; is-var-set 3 # var[0] set to 'foo' var=(); is-var-set 4 # var unset (all indices) var=(foo); is-var-set 5 # var[0] set to 'foo' var=([0]=foo); is-var-set 6 # var[0] set to 'foo' var=([1]=foo); is-var-set 7 # var[0] unset, but var[1] set to 'foo' declare -a var; is-var-set 8 # var empty, but declared as an array declare -A var; is-var-set 9 # var empty, but declared as an associative array declare -A var # Because is-var-set() conveniently unsets it var=([xz]=foo); is-var-set A # var[xz] set to 'foo', but var's otherwise empty declare -a var # Demonstrate that Bash knows about var, even when there's declare -A var; is-var-set B # apparently no way to just _check_ its existence
这是输出。
1: var's unset. ${var+x}= ${#var[@]}=0 2: var is set. ${var+x}=x ${#var[@]}=1 3: var is set. ${var+x}=x ${#var[@]}=1 4: var's unset. ${var+x}= ${#var[@]}=0 5: var is set. ${var+x}=x ${#var[@]}=1 6: var is set. ${var+x}=x ${#var[@]}=1 7: Is var set? ${var+x}= ${#var[@]}=1 8: var's unset. ${var+x}= ${#var[@]}=0 9: var's unset. ${var+x}= ${#var[@]}=0 A: Is var set? ${var+x}= ${#var[@]}=1 ./foo.sh: line 26: declare: var: cannot convert indexed to associative array B: var's unset. ${var+x}= ${#var[@]}=0
总共:
- 在大多数情况下,
${var+x}
参数扩展语法与${#var[@]}
数组语法一样工作,例如检查函数的参数。 这种情况可能会破坏的唯一方法是如果未来版本的Bash添加了一种将数组传递给函数而不将内容转换为单个参数的方法。 - 数组语法对于元素
0
设置的非空数组(非关联)是必需的。 - 这两种语法都不能解释发生了什么,如果
declare -a var
已经被使用,甚至没有在数组的某个地方分配一个空值。 Bash仍然将案件区分开来(如上面的testingB
所示),所以这个答案不是万无一失的。 幸运的是,Bash在运行程序/脚本时将导出的环境variables转换为string,所以任何声明但未设置variables的问题都将被包含到单个脚本中,至less如果它不包含其他脚本。
如果你想testing一个variables是绑定的还是非绑定的,即使在你打开了这个数组选项之后,
set -o noun set if printenv variableName >/dev/null; then # variable is bound to a value else # variable is unbound fi
[[ $foo ]]
要么
(( ${#foo} ))
要么
let ${#foo}
要么
declare -p foo
我喜欢辅助function来隐藏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} ] }
Because I first had bugs in this implementation (inspired by the answers of Jens and Lionel), I came up with a different solution:
# Ask for the properties of the variable - fails if not declared function IsDeclared() { declare -p $1 &>/dev/null }
I find it to be more straight-forward, more bashy and easier to understand/remember. Test case shows it is equivalent:
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
The test case also shows that local var
does NOT declare var (unless followed by '='). For quite some time I thought i declared variables this way, just to discover now that i merely expressed my intention… It's a no-op, i guess.
IsDeclared_Tricky xyz: 1
IsDeclared_Tricky foo: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
IsDeclared foo: 1
IsDeclared bar: 0
IsDeclared baz: 0
BONUS: usecase
I mostly use this test to give (and return) parameters to functions in a somewhat "elegant" and safe way (almost resembling an interface…):
#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 error exampleFunction echo $outputStr } bonus
If called with all requires variables declared:
你好,世界!
其他:
Error: variable not declared: outputStr
Functions to check if variable is declared/unset
including empty $array=()
The following functions test if the given name exists as a variable
# 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;} }
- By first testing if the variable is (un)set, the call to declare can be avoided, if not necessary.
- If however
$1
contains the name of an empty$array=()
, the call to declare would make sure we get the right result - There's never much data passed to /dev/null as declare is only called if either the variable is unset or an empty array.
This functions would test as showed in the following conditions:
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
。
For more details
and a test script see my answer to the question "How do I check if a variable exists in bash?" 。
Remark: The similar usage of
declare -p
, as it is also shown by Peregring-lk 's answer , is truly coincidental. Otherwise I would of course have credited it!
case "$1" in "") echo "blank";; *) echo "set" esac