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

发布于 2024-12-10 15:51:17 字数 187 浏览 0 评论 0原文

以下代码退出并出现未绑定变量错误。如何在仍然使用 set -o nounset 选项的情况下解决此问题?

#!/bin/bash

set -o nounset

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

echo "whatever"

The following code exits with a unbound variable error. How can I fix this, while still using the set -o nounset option?

#!/bin/bash

set -o nounset

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

echo "whatever"

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(8

半窗疏影 2024-12-17 15:51:17
#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

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

echo "whatever"

在这种情况下,如果未设置 WHATEVERVALUE 最终将成为空字符串。我们使用的是 {parameter:-word} 扩展,您可以在“参数扩展”下的 man bash 中查找该扩展。

#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

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

echo "whatever"

In this case, VALUE ends up being an empty string if WHATEVER is not set. We're using the {parameter:-word} expansion, which you can look up in man bash under "Parameter Expansion".

翻身的咸鱼 2024-12-17 15:51:17

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

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

You need to quote the variables if you want to get the result you expect:

check() {
    if [ -n "${WHATEVER-}" ]
    then
        echo 'not empty'
    elif [ "${WHATEVER+defined}" = defined ]
    then
        echo 'empty but defined'
    else
        echo 'unset'
    fi
}

Test:

$ unset WHATEVER
$ check
unset
$ WHATEVER=
$ check
empty but defined
$ WHATEVER='   '
$ check
not empty
顾北清歌寒 2024-12-17 15:51:17

假设:

$ 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.

您甚至可以使用 [ 和 < code>] 而不是上面的 [[]],但后者在 Bash 中更可取。

注意上面冒号的作用。来自文档

换句话说,如果包含冒号,则运算符会测试两个参数是否存在以及其值是否不为空;如果省略冒号,则运算符仅测试是否存在。

显然不需要 -n-z

总而言之,我通常只使用 [[ "${VAR:?}" ]] 根据示例,如果变量为 null,则会打印错误并退出或未设置。

Assumptions:

$ 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

If you want a non-interactive script to print an error and exit if a variable is null or not set:

$ [[ "${HOME:?}" ]]

$ [[ "${IAMUNBOUND:?}" ]]

bash: IAMUNBOUND: parameter null or not set

$ IAMNULL=""
$ [[ "${IAMNULL:?}" ]]

bash: IAMNULL: parameter null or not set

If you don't want the script to exit:

$ [[ "${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.

You can even use [ and ] instead of [[ and ]] above, but the latter is preferable in Bash.

Note what the colon does above. From the documentation:

Put another way, if the colon is included, the operator tests for both parameter’s existence and that its value is not null; if the colon is omitted, the operator tests only for existence.

There is apparently no need for -n or -z.

In summary, I may typically just use [[ "${VAR:?}" ]]. Per the examples, this prints an error and exits if a variable is null or not set.

你另情深 2024-12-17 15:51:17

使用 oneliner:

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

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

Use a oneliner:

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

-z checks both for empty or unset variable

轻拂→两袖风尘 2024-12-17 15:51:17

您可以使用

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

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

可能更具可读性。

You can use

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

but

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

might be more readable.

她如夕阳 2024-12-17 15:51:17

虽然这完全不是所要求的用例,但我发现如果您想使用nounset(或-u)默认行为是您想要的行为:以描述性消息退出非零值。

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

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

While this isn't exactly the use case asked for, I've found that if you want to use nounset (or -u) the default behavior is the one you want: to exit nonzero with a descriptive message.

If all you want is to echo something else when exiting, or do some cleanup, you can use a trap.

The :- operator is probably what you want otherwise.

不语却知心 2024-12-17 15:51:17

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

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
  • Check1Check2 的行为相同
  • 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

To me, most of the answers are at best confusing, not including a test matrix. They also often do not address the scenario where variable contains the defaulting value.

The solution from l0b0 is the only readable, testable (and correct in respect to the actual question IMO), but it is unclear if inverting/reordering the tests to simplify the logic produces correct result. I minified hirs solution

The (already minified) contrast solution from Aleš, exposes the difference of a variable being declared but undefined. The one or the other might fit your scenario.

#!/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 and Check2 behave identically
  • Check3 is plainly wrong
  • Check4: correct, depending on what you consider a declared/defined variable.
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

趁年轻赶紧闹 2024-12-17 15:51:17

这是我的贡献,当我开始使用数组/关联数组时,我需要一种简单的方法来获得答案。我的问题是:对于 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 "    $? varstat outcome" #uncomment to verify 100% successes
        echo "" #just formatting output
    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 with error if this is a required variable; else safe to succeed'
    return 1
fi

declare -a my_empty_array  #or declare -A my_empty_array 
if [[ $( var_stat my_empty_array )  -le 0 ]]; then
    echo 'exit with error if an array (associative or not) must have at least one key/element; else safe to succeed'
    return 1
fi

This is my contribution bc once I began working with arrays/associative arrays, I needed an easy way to get the answer. My issue was: with 0 element arrays, this would return an error:

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
}

Unit Test:

#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 "    $? varstat outcome" #uncomment to verify 100% successes
        echo "" #just formatting output
    done

Output:

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

Practical examples:

if [[ $( var_stat my_unset_var ) -eq -1 ]]; then
    echo 'exit with error if this is a required variable; else safe to succeed'
    return 1
fi

declare -a my_empty_array  #or declare -A my_empty_array 
if [[ $( var_stat my_empty_array )  -le 0 ]]; then
    echo 'exit with error if an array (associative or not) must have at least one key/element; else safe to succeed'
    return 1
fi

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文