以下代码退出并出现未绑定变量错误。我怎样才能解决这个问题,同时仍然使用
set -o
名词集选项?
#!/bin/bash
set -o nounset
if [ ! -z ${WHATEVER} ];
then echo "yo"
fi
echo "whatever"
#!/bin/bash
set -o nounset
VALUE=${WHATEVER:-}
if [ ! -z ${VALUE} ];
then echo "yo"
fi
echo "whatever"
在这种情况下,如果未设置
VALUE
,则 WHATEVER
最终会成为空字符串。我们正在使用 {parameter:-word}
扩展,您可以在“参数扩展”下的 man bash
中查找。
如果你想得到你期望的结果,你需要引用变量:
check() {
if [ -n "${WHATEVER-}" ]
then
echo 'not empty'
elif [ "${WHATEVER+defined}" = defined ]
then
echo 'empty but defined'
else
echo 'unset'
fi
}
测试:
$ unset WHATEVER
$ check
unset
$ WHATEVER=
$ check
empty but defined
$ WHATEVER=' '
$ check
not empty
假设:
$ echo $SHELL
/bin/bash
$ /bin/bash --version | head -1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
$ set -o nounset
如果您希望非交互式脚本在变量为空或未设置时打印错误并退出:
$ [[ "${HOME:?}" ]]
$ [[ "${IAMUNBOUND:?}" ]]
bash: IAMUNBOUND: parameter null or not set
$ IAMNULL=""
$ [[ "${IAMNULL:?}" ]]
bash: IAMNULL: parameter null or not set
如果您不希望脚本退出:
$ [[ "${HOME:-}" ]] || echo "Parameter null or not set."
$ [[ "${IAMUNBOUND:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.
$ IAMNULL=""
$ [[ "${IAMUNNULL:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.
您甚至可以使用
[
和 ]
代替上面的 [[
和 ]]
,但后者在 Bash 中更可取。
注意上面冒号的作用。来自文档:
换句话说,如果包含冒号,则运算符会测试两个参数是否存在且其值不为空;如果省略冒号,则运算符仅测试是否存在。
显然不需要
-n
或 -z
。
总之,我通常可能只使用
[[ "${VAR:?}" ]]
。 根据示例,如果变量为 null 或未设置,则会打印错误并退出。
使用眼线笔:
[ -z "${VAR:-}" ] && echo "VAR is not set or is empty" || echo "VAR is set to $VAR"
-z
检查空或未设置的变量
你可以使用
if [[ ${WHATEVER:+$WHATEVER} ]]; then
但是
if [[ "${WHATEVER:+isset}" == "isset" ]]; then
可能更具可读性。
虽然这不是完全所要求的用例,但我发现如果您想使用
nounset
(或-u
),默认行为就是您想要的:使用描述性消息退出非零.
如果您只想在退出时回显其他内容,或进行一些清理,则可以使用陷阱。
:-
运算符可能就是您想要的。
对我来说,大多数答案充其量都是令人困惑的,不包括测试矩阵。他们通常也没有解决变量包含默认值的情况。
来自l0b0的解决方案是唯一可读、可测试的(并且对于实际问题而言是正确的),但尚不清楚反转/重新排序测试以简化逻辑是否会产生正确的结果。我缩小了他的解决方案
Aleš 的(已经缩小的)对比解决方案暴露了已声明但未定义的变量的差异。其中之一可能适合您的场景。
#!/bin/bash -eu
check1() {
if [[ -n "${WHATEVER-}" ]]; then
echo 'something else: not empty'
elif [[ "${WHATEVER+defined}" = defined ]]; then
echo 'something else: declared but undefined'
else
echo 'unset'
fi
}
check2() {
if [[ "${WHATEVER+defined}" != "defined" ]]; then
echo 'unset'
else
echo "something else"
fi
}
check3() {
if [[ "${WHATEVER-defined}" = defined ]]; then
echo 'unset'
else
echo 'something else'
fi
}
check4() {
if [[ ! ${WHATEVER+$WHATEVER} ]]; then
echo 'unset'
else
echo 'something else'
fi
}
echo
echo "check1 from l0b0"
unset WHATEVER
check1
WHATEVER=
check1
WHATEVER=' '
check1
WHATEVER='defined'
check1
echo
echo "check2 prove simplification keeps semantics"
unset WHATEVER
check2
WHATEVER=
check2
WHATEVER=' '
check2
WHATEVER='defined'
check2
echo
echo "check3 other promising operator?"
unset WHATEVER
check3
WHATEVER=
check3
WHATEVER=' '
check3
WHATEVER='defined'
check3
echo
echo "check4 other interesting suggestion, from aleš"
unset WHATEVER
check4
WHATEVER=
check4
WHATEVER=' '
check4
WHATEVER='defined'
check4
Check1
和
Check2
的行为相同
Check3
显然是错误的
Check4
:正确,取决于您对声明/定义变量的看法。
check1 from l0b0
unset
something else: declared but undefined
something else: not empty
something else: not empty
check2 prove simplification keeps semantics
unset
something else
something else
something else
check3 other promising operator?
unset
something else
something else
unset
check4 other interesting suggestion, from aleš
unset
unset
something else
something else
if [ ${#my_array[@]} -eq 0 ]; then #this will error if nounset is enabled!!
echo "return error due to empty array"
return 1
fi
var_stat() {
#Determines if a variable (or array) is unset, blank (or no keys) or populated (has key(s))
#Input: single parameter to be text of a variable name or an array or an associative array
#stdout depends on whether the input represents a variable (or array) that is:
# -1: unset
# 0: set and blank (or set with 0 keys)
# 1: set and not blank (or set and has key(s))
local input_var_name="${1:-"__NA"}"
if [[ "${input_var_name:-"__NA"}" = "__NA" ]]; then
echo -1
return 0
fi
#evaluate results of declare -p
case "$( declare -p ${input_var_name} 2>/dev/null || echo "__NA" )" in
#if begins with __NA then above command failed and variable is unset
__NA*)
echo -1
;;
*\))
#if ending with ) (escaped as '\)') then it is an array and populated
echo 1
;;
declare\ -[aA]*)
#if it begins with declare -a or declare -A it is an empty array (bc it failed the prior test)
echo 0
;;
#otherwise, it is a regular variable; z test is safe
*)
if [ -z "${!input_var_name}" ]; then #"${!input_var_name}" is indirect variable usage
#it is empty
echo 0
else
#it is not empty
echo 1
fi
esac
}
单元测试:
#Usage - tested with both errexit/nounset on/off
#first, define a bunch of variables, some blank, some keyless, etc.
mysettext="settext"
myblanktext=""
declare -A myemptyAA
declare -a myemptya
declare -A myhaskeyAA
myhaskeyAA[oneElement]="associative array with one key"
declare -a myhaskeya
myhaskeya+=("array with one value")
declare -i myemptyinteger
declare -i mysetinteger
mysetinteger=3
#regular array to store the variable names of our tests
unset var_stat_test_array
declare -a var_stat_test_array
var_stat_test_array+=('mysettext')
var_stat_test_array+=('myblanktext')
var_stat_test_array+=('myemptyAA')
var_stat_test_array+=('myemptya')
var_stat_test_array+=('myhaskeyAA')
var_stat_test_array+=('myhaskeya')
var_stat_test_array+=('myemptyinteger')
var_stat_test_array+=('mysetinteger')
var_stat_test_array+=('myUNSET') #this is not set per above
#cycle through the test array, run declare -p then the var_stat function etc.
for eachElement in "${var_stat_test_array[@]}"; do
declare -p ${eachElement} || true #guarantee we succeed even if unset
var_stat "${eachElement}" | sed "s/^/ var_stat returned:/"
echo ""
# echo " $? varstat outcome" #uncomment to verify 100% successes
done
输出:
declare -- mysettext="settext"
var_stat returned:1
declare -- myblanktext=""
var_stat returned:0
declare -A myemptyAA
var_stat returned:0
declare -a myemptya
var_stat returned:0
declare -A myhaskeyAA=([oneElement]="associative array with one key" )
var_stat returned:1
declare -a myhaskeya=([0]="array with one value")
var_stat returned:1
declare -i myemptyinteger
var_stat returned:0
declare -i mysetinteger="3"
var_stat returned:1
-bash: declare: myUNSET: not found
var_stat returned:-1
实际例子:
if [[ $( var_stat my_unset_var ) -eq -1 ]]; then
echo 'exit if this is a required variable; else safe to succeed'
fi
declare -a my_empty_array #or declare -A my_empty_array
if [[ $( var_stat my_empty_array ) -le 0 ]]; then
echo 'exit if an array (associative or not) must have at least one key/element; else safe to succeed'
fi