我如何知道是否在Bash中设置了变量?
例如,如何检查用户是否向函数提供了第一个参数?
function a {
# if $1 is set ?
}
我如何知道是否在Bash中设置了变量?
例如,如何检查用户是否向函数提供了第一个参数?
function a {
# if $1 is set ?
}
当前回答
Note
由于Bash标签,我给出了一个非常注重Bash的答案。
简短的回答
只要您只处理Bash中的命名变量,这个函数应该始终告诉您变量是否已设置,即使它是一个空数组。
variable-is-set() {
declare -p "$1" &>/dev/null
}
为什么这样做
在Bash(至少可以追溯到3.0)中,如果var是一个声明的/set变量,那么declare-p var输出一个声明命令,将变量var设置为当前的类型和值,并返回状态代码0(成功)。如果var未声明,则declare-p var将向stderr输出错误消息并返回状态代码1。使用&>/dev/null,将常规stdout和stderr输出重定向到/dev/null,永不可见,并且不更改状态代码。因此,函数只返回状态代码。
为什么其他方法(有时)在Bash中失败
[-n“$var”]:仅检查${var[0]}是否为非空。(在Bash中,$var与${var[0]}相同。)[-n“${var+x}”]:仅检查是否设置了${var[0]}。[“${#var[@]}”!=0]:仅检查是否至少设置了$var的一个索引。
当此方法在Bash中失败时
这只适用于命名变量(包括$_),而不适用于某些特殊变量($!,$@,$#,$$,$*,$?,$-,$0,$1,$2,…,以及我可能忘记的任何变量)。由于这些都不是数组,POSIX样式[-n“${var+x}”]适用于所有这些特殊变量。但请注意不要将其包装在函数中,因为在调用函数时,许多特殊变量会更改值/存在性。
外壳兼容性说明
如果您的脚本有数组,并且试图使其与尽可能多的shell兼容,那么考虑使用typeset-p而不是declare-p。我读到ksh只支持前者,但还没有能够测试这一点。我知道Bash 3.0+和Zsh 5.5.1都支持typeset-p和declare-p,不同之处仅在于其中一个是另一个的替代。但除了这两个关键字之外,我还没有测试过它们的差异,也没有测试过其他外壳。
如果您需要脚本与POSIX sh兼容,那么就不能使用数组。如果没有数组,[-n“{$var+x}”]可以工作。
Bash中不同方法的比较代码
此函数取消设置变量var,对传递的代码求值,运行测试以确定var是否由evald代码设置,最后显示不同测试的结果状态代码。
我跳过了测试-vvar、[-vvar]和[[-vvar]],因为它们产生了与POSIX标准[-n“${var+x}”]相同的结果,同时需要Bash 4.2+。我也跳过了typeset-p,因为它与我测试过的shell(Bash 3.0到5.0和Zsh 5.5.1)中的声明-p相同。
is-var-set-after() {
# Set var by passed expression.
unset var
eval "$1"
# Run the tests, in increasing order of accuracy.
[ -n "$var" ] # (index 0 of) var is nonempty
nonempty=$?
[ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
plus=$?
[ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
count=$?
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 0
is-var-set-after "var=(foo)" # 0 0 0 0
is-var-set-after "var=([0]=foo)" # 0 0 0 0
is-var-set-after "var=([x]=foo)" # 0 0 0 0
is-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 0
is-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 0
is-var-set-after "var=([1]=foo)" # 1 1 0 0
is-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 0
is-var-set-after "declare -a var" # 1 1 1 0
is-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],并分别声明-p var。
test: -n +x #@ -p
var=foo: 0 0 0 0
var=(foo): 0 0 0 0
var=([0]=foo): 0 0 0 0
var=([x]=foo): 0 0 0 0
var=([y]=bar [x]=foo): 0 0 0 0
var='': 1 0 0 0
var=([0]=''): 1 0 0 0
var=([1]=''): 1 1 0 0
var=([1]=foo): 1 1 0 0
declare -A var; var=([x]=foo): 1 1 0 0
var=(): 1 1 1 0
declare -a var: 1 1 1 0
declare -A var: 1 1 1 0
unset var: 1 1 1 1
总结
声明-p var&>/dev/null对于测试Bash中的命名变量是(100%?)可靠的,至少从3.0开始。[-n“${var+x}”]在符合POSIX的情况下是可靠的,但不能处理数组。其他测试用于检查变量是否为非空,以及检查其他shell中声明的变量。但这些测试既不适合Bash也不适合POSIX脚本。
其他回答
用于检查变量是否已声明/未设置的函数
包括空$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;}
}
通过首先测试变量是否已(未)设置,可以避免对声明的调用(如果不需要的话)。然而,如果$1包含空$array=()的名称,则调用声明将确保获得正确的结果传递给/dev/null的数据不多,因为只有在变量未设置或为空数组时才会调用declare。
该功能将在以下条件下进行测试:
a、 #未声明a=;#已声明a=“foo”;#已声明a=();#已声明a=(“”);#已声明取消设置a;#未声明a、 #未设置a=;#未设置a=“foo”;#未设置a=();#未设置a=(“”);#未设置取消设置a;#未设置
.
有关详细信息一个测试脚本可以看到我对“如何检查bash中是否存在变量?”问题的回答。备注:declare-p的类似用法,正如Peregring lk的答案所示,确实是巧合。否则我当然会把它归功于你!
if [ "$1" != "" ]; then
echo \$1 is set
else
echo \$1 is not set
fi
尽管对于参数,通常最好测试$#,我认为这是参数的数量。
if [ $# -gt 0 ]; then
echo \$1 is set
else
echo \$1 is not set
fi
如果你想检查$@中的任何内容,我找到了一个更好的代码。
if [[ $1 = "" ]] then echo '$1 is blank' else echo '$1 is filled up' fi
为什么会这样?$@中的所有内容都存在于Bash中,但默认情况下为空,因此test-z和test-n无法帮助您。
更新:您还可以计算参数中的字符数。
if [ ${#1} = 0 ] then echo '$1 is blank' else echo '$1 is filled up' fi
case "$1" in
"") echo "blank";;
*) echo "set"
esac
如果你和我一样,你所寻找的其实是
“如果设置了变量,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