【问题标题】:Test for a Bash variable being unset, using a function使用函数测试未设置的 Bash 变量
【发布时间】:2010-10-26 20:09:52
【问题描述】:

一个简单的 Bash 变量测试:

${varName:?    "${varName} is not defined"}

我想重新使用它,把它放在一个函数中。 请问怎么样?

以下失败

#
# Test a variable exists
tvar(){
 val=${1:?    "${1}    must be defined, preferably in $basedir"}
 if [ -z ${val}  ]
     then 
     echo Zero length value 
 else
     echo ${1} exists, value ${1}
 fi
}

即如果测试失败,我需要退出。

【问题讨论】:

  • 您的标题显示“未设置”,但您的示例测试“未设置或设置为空字符串”。你关心哪种情况?

标签: bash variables undefined


【解决方案1】:

感谢 lhunath 的回答,我被引导到 Bash man 页面的一部分,我已经忽略了数百次:

当不执行子字符串扩展时,bash 测试一个参数 未设置或为空;省略冒号会导致仅对参数进行测试 三是未设置。

这促使我创建了以下真值表:

|未设置 |设置 |设置和 |意义 | |但为空|不为空 | ============+========+===========+==========+======= ======================= ${var-_} | T | F | T |不为空或未设置 ------------+--------+----------+----------+-------- ---------------------- ${var:-_} | T | T | T |总是正确的,用于替代。 ------------+--------+----------+----------+-------- ---------------------- $变量 | F | F | T | var 已设置且不为空 ------------+--------+----------+----------+-------- ---------------------- ${!var[@]} | F | T | T | var 已设置

此表在最后一行介绍了规格。 Bash man 页面显示“如果 name 不是数组,如果 name 已设置则扩展为 0,否则扩展为 null。”出于这个真值表的目的,即使它是一个数组,它的行为也是一样的。

【讨论】:

  • 非常酷,但是你知道为什么当 var 是数组时会失败:test "${!var[@]}" && echo 'T' || echo 'F',错误消息binary operator expected
  • @l0b0:它给了我一个“T”或一个“F”,这取决于 var 是设置还是取消设置。什么外壳(及其版本)? (这只适用于 Bash。)${var[@]} 的内容是什么(declare -p var 的输出)?
  • Bash 4.1.5(1)-release (i486-pc-linux-gnu), declare -a var='([0]="var1" [1]="var2" [2]="var3")'。见gist
  • @l0b0:哦,抱歉,test(又名[)不支持与[[ 相同的功能。试试[[ ${!var[@]}" ]] && echo 'T' || echo 'F'。另见thisthis
  • @Dennis:这不会失败,但即使未设置也会产生true(参见要点)
【解决方案2】:

您正在寻找的是间接性。

assertNotEmpty() {
    : "${!1:? "$1 is empty, aborting."}"
}

如果您执行以下操作,则会导致脚本中止并显示错误消息:

$ foo=""
$ assertNotEmpty foo
bash: !1:  foo is empty, aborting.

如果您只想测试 foo 是否为空,请使用此函数而不是函数,而不是中止脚本:

[[ $foo ]]

例如:

until read -p "What is your name? " name && [[ $name ]]; do
    echo "You didn't enter your name.  Please, try again." >&2
done

另外,请注意 emptyunset 参数之间存在非常重要的区别。您应该注意不要混淆这些术语!空参数是已设置但仅设置为空字符串的参数。未设置的参数是根本不存在的参数。

前面的示例都测试了 empty 参数。如果您想测试 unset 参数并认为所有设置的参数都可以,无论它们是否为空,请使用:

[[ ! $foo && ${foo-_} ]]

在这样的函数中使用它:

assertIsSet() {
    [[ ! ${!1} && ${!1-_} ]] && {
        echo "$1 is not set, aborting." >&2
        exit 1
    }
}

仅当您传递的参数名称表示未设置的参数时才会中止脚本:

$ ( foo="blah"; assertIsSet foo; echo "Still running." )
Still running.
$ ( foo=""; assertIsSet foo; echo "Still running." )
Still running.
$ ( unset foo; assertIsSet foo; echo "Still running." )
foo is not set, aborting.

【讨论】:

  • 这就是我想要的。谢谢。我应该说未定。所以我想要的功能是 assertIsSet() { [[ ! ${!1} && ${!1-_} ]] && { echo "$1 未设置,正在中止。" >&2 exit 1 } } 谢谢。
  • 对于较新的 bash,测试 [[ ! -v var ]] 只有在未设置 var 时才会为真。看起来比assertIsSet 中的双重测试更容易。 :)
【解决方案3】:

你想使用[ -z ${parameter+word} ]

man bash的一部分:

Parameter Expansion
    ...
    In  each  of  the cases below, word is subject to tilde expansion, parameter expansion, command substitution, and
    arithmetic expansion.  When not performing substring expansion, bash tests for a parameter that is unset or null;
    omitting the colon results in a test only for a parameter that is unset.
    ...
    ${parameter:+word}
           Use Alternate Value.  If parameter is null or unset, nothing is substituted, otherwise  the  expansion  of
           word is substituted.
    ...

换句话说:

    ${parameter+word}
           Use Alternate Value.  If parameter is unset, nothing is substituted, otherwise  the  expansion  of
           word is substituted.

一些例子:

$ set | grep FOOBAR
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
it is unset
$ declare FOOBAR
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
$ FOOBAR=
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
$ FOOBAR=1
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
$ unset FOOBAR
$ if [ -z "${FOOBAR+something}" ]; then echo "it is unset"; fi
it is unset
$

【讨论】:

  • “set | grep FOOBAR”有什么作用?
【解决方案4】:

此函数测试当前设置的变量。变量甚至可以是一个数组。请注意,在 bash 中:0 == TRUE,1 == FALSE。

function var.defined {
    eval '[[ ${!'$1'[@]} ]]'
}

# Typical Usage of var.defined {}

declare you="Your Name Here" ref='you';

read -p "What's your name: " you;

if var.defined you; then   # Simple demo using literal text

    echo "BASH recognizes $you";
    echo "BASH also knows a reference to $ref as ${!ref}, by indirection."; 

fi

unset you # have just been killed by a master :D

if ! var.defined $ref; then    # Standard demo using an expanded literal value

    echo "BASH doesn't know $ref any longer";

fi

read -s -N 1 -p "Press any key to continue...";
echo "";

所以在这里要清楚,该函数测试文字文本。每次在 bash 中调用命令时,变量通常会被基础值“换出”或“替换”,除非:

  • $varRef ($) 被转义:\$varRef
  • $varRef 是单引号'$varRef'

【讨论】:

  • 命令eval 是一个非常危险的命令,特别是当您扩展用户(名称)设置的变量时。更好用:[[ -v name ]],在 bash 内部,使用起来更安全。 :)
【解决方案5】:

我不完全了解您需要什么。尽管如此,我还是想回复你。

即如果测试失败,我需要退出。

代码:

${varName:?    "${varName} is not defined"}

当没有名为“varName”的变量时,将返回一个非零退出代码。最后一条命令的退出码保存在$?中。

关于您的代码:

val=${1:?    "${1}    must be defined, preferably in $basedir"}

也许它没有做你需要的。在$1 未定义的情况下,"${1}" 将被替换为空。可能您想使用单引号,字面上写成${1} 而无需替换。

val=${1:?    '${1}    must be defined, preferably in $basedir'

【讨论】:

  • 我想将测试封装在一个函数中,允许我使用定义的 SomeVariableName 调用它,以便我可以重用它。
  • 我完成的函数,测试未设置和空(带和大括号)assertIsSet2() { if [[ ! ${!1} && ${!1-_} ]] 然后回显“$1 未设置,正在中止。” >&2 exit 1 #else # echo ${1} is set, value [${1}] fi : "${!1:? "$1 is empty, aborting."}" } 感谢您的输入。我回到了正轨!
【解决方案6】:

不确定这是否正是您想要的,但我在编写新的+复杂脚本时使用的一个方便技巧是使用“set -o”

set -o #当脚本找到未设置的变量时,将使其爆炸

EG:

$ grep '$1' chex.sh

案例“$1”

$ ./chex.sh

./chex.sh:第 111 行:$1:未绑定变量

$ ./chex.sh foo

不正确/未通过任何选项.. 退出

【讨论】:

  • 你的意思是set -uset -o 用于设置选项,例如 set -o pipefail(您也应该使用)。
  • 'set -u' 与 'set -o nounset' 相同,这可能是上面的意图。
【解决方案7】:
if set | grep -q '^VARIABLE='
then 
    echo VARIABLE is set
fi

【讨论】:

  • 分叉和使用外部命令只是为了检查变量集是否非常昂贵。
猜你喜欢
  • 2010-12-25
  • 2011-12-11
  • 2016-08-11
  • 1970-01-01
  • 1970-01-01
  • 2017-04-10
  • 1970-01-01
  • 2018-10-24
相关资源
最近更新 更多