使用“set -o nounset”测试是否在 Bash 中设置了变量

问题描述 投票:0回答:8

以下代码退出并出现未绑定变量错误。我怎样才能解决这个问题,同时仍然使用

set -o
名词集选项?

#!/bin/bash

set -o nounset

if [ ! -z ${WHATEVER} ];
 then echo "yo"
fi

echo "whatever"
bash shell unix
8个回答
140
投票
#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

if [ ! -z ${VALUE} ];
 then echo "yo"
fi

echo "whatever"

在这种情况下,如果未设置

VALUE
,则
WHATEVER
最终会成为空字符串。我们正在使用
{parameter:-word}
扩展,您可以在“参数扩展”下的
man bash
中查找。


43
投票

如果你想得到你期望的结果,你需要引用变量

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

14
投票

假设:

$ 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 或未设置,则会打印错误并退出。


12
投票

使用眼线笔:

[ -z "${VAR:-}" ] && echo "VAR is not set or is empty" || echo "VAR is set to $VAR"

-z
检查空或未设置的变量


7
投票

你可以使用

if [[ ${WHATEVER:+$WHATEVER} ]]; then

但是

if [[ "${WHATEVER:+isset}" == "isset" ]]; then

可能更具可读性。


1
投票

虽然这不是完全所要求的用例,但我发现如果您想使用

nounset
(或
-u
),默认行为就是您想要的:使用描述性消息退出非零.

如果您只想在退出时回显其他内容,或进行一些清理,则可以使用陷阱。

:-
运算符可能就是您想要的。


1
投票

对我来说,大多数答案充其量都是令人困惑的,不包括测试矩阵。他们通常也没有解决变量包含默认值的情况。

来自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
    

0
投票
这是我的贡献,当我开始使用数组/关联数组时,我需要一种简单的方法来获得答案。我的问题是:对于 0 个元素数组,这将返回错误:

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
    
© www.soinside.com 2019 - 2024. All rights reserved.