测试是否设置了多个变量


19

我想确保在脚本的某个点上,在访问source配置文件后,设置了几个变量,如果没有设置,将停止执行,以告知用户缺少的变量。我试过了

for var in $one $two $three ; do
    ...

但是,例如$two,如果未设置,则永远不会为执行该循环$two。我接下来尝试的是

for var in one two three ; do
    if [ -n ${!var} ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

但是,如果没有设置两个,我仍然会得到“两个设置为”而不是“两个未设置”。

如何确保设置了所有必需的变量?

更新/解决方案:我知道“设置”和“设置,但为空”之间存在区别。我现在使用(感谢/programming//a/16753536/3456281和此问题的答案)以下内容:

if [ -n "${!var:-}" ] ; then

因此,如果var设置但为空,则仍视为无效。


1
您还可以添加set -u到脚本的开头,以在使用unset变量时立即终止脚本。
2014年

Answers:


8

报价错误。

if [ -n "${!var}" ] ; then

面向未来:环境

set -x

在运行代码之前,可能会向您显示问题。无需将其添加到代码中,您可以使用

bash -vx ./my/script.sh

这行得通,但是没有引号会发生什么?我的一般方法是正确的吗?
贾斯珀2014年

是的,这是有先见之明的:在回答之前,我已经回答了这个问题... 8
Hauke Laging

4
@Jasper您应该始终引用变量。这比花时间思考是否每次都花更多的时间。但这避免了错误。
Hauke Laging

相反,引用只能防止扩展。如果扩展是您感兴趣的,那么报价当然不是可行的方法。例如:cs = 673,290,765 ,;设置-$(IFS =,; echo $ cs); 回声$#; 输出:3
mikeserv

2
@mikeserv只有单引号可以防止扩展,而我显然不建议这样做。双引号可防止单词拆分。我没有否认在某些情况下必须省略报价。但这不是反对我的一般规则的论点。什么样的人会使用类似的东西set -- $(IFS=, ; echo $cs)?那种不得不在这里问为什么if [ -n ${!var} ] ; then不起作用的人?可能不会。
Hauke Laging

5

您唯一需要的是测试中的引号:

for var in one two three ; do
    if [ -n "${!var}" ] ; then
        echo "$var is set to ${!var}"
    else
        echo "$var is not set"
    fi
done

为我工作。


4

如果您希望程序停止:

N= 
${one?var 1 is unset} 
${two:?var 2 is unset or null}
${three:+${N:?var 3 is set and not null}}

这样就可以了。问号后的每条消息都被打印到stderr并且父shell 死亡。好吧,好的,所以不是每个消息-只有一个-只是第一个失败的消息都会打印一条消息,导致外壳死亡。我喜欢这样使用这些测试:

( for v in "$one" "$two" "$three" ; do
    i=$((i+1)) ; : ${v:?var $i is unset or null...} 
done ) || _handle_it

我有很多更说这个在这里


2

你可以加

set -u

到脚本的开头,以使其在尝试使用未设置的变量时终止。

像这样的脚本

#!/bin/sh
set -u
echo $foo

将导致

script.sh:3:sc​​ript.sh:foo:未设置参数

如果使用的bash是错误,则错误将如下所示:

script.sh:第3行:foo:未绑定变量


您认为这对于运行时检查有意义吗?
Hauke Laging

2
@HaukeLaging我不太了解您- set -u可以防止OP试图避免的那种错误,并且(与所有其他解决方案相反)不限于一组特定的变量。实际上,对于几乎所有的shell脚本来说,使它们安全地失败是有用的预防措施,而不是在未设置变量时执行意外的操作。本文是该主题的便捷参考。
2014年

@ n.st我不同意-如果您打算为null设置一个值,则该值与不为null一样有用。
mikeserv

1
@ n.st这对于OP没有任何意义,因为他不希望防止访问未设置的变量(顺便说一句:设置但为空的变量会导致相同的错误,但对您的“保护”也不起作用)。他想在运行时检查变量是否未设置/为空。您的建议可能对总体开发有所帮助,但不能解决OP的问题。
Hauke Laging

set -u我认为也可以使用它,但是它不如适当的参数扩展灵活(请参阅我更新的问题)。并与set -u我不得不做出一个虚拟的访问到所有目标变量,如果我要检查在一个地方他们的存在。
贾斯珀2014年

2

一种最大程度友好的解决方案可以测试所有需求,并将它们一起报告,而不是在第一个失败时就反复要求正确地解决问题:

#!/bin/bash

required_vars=(one two three)

missing_vars=()
for i in "${required_vars[@]}"
do
    test -n "${!i:+y}" || missing_vars+=("$i")
done
if [ ${#missing_vars[@]} -ne 0 ]
then
    echo "The following variables are not set, but should be:" >&2
    printf ' %q\n' "${missing_vars[@]}" >&2
    exit 1
fi

我使用数组变量来跟踪尚未设置的变量,然后使用结果来编写面向用户的消息。

笔记:

  • ${required_vars[@]}for循环中引用的原因主要是出于习惯-我不建议在您的变量名中包含任何shell元字符!
  • 我没有引用${#missing_vars[@]},因为这总是一个整数,即使您很反常而忽略了前面的建议。
  • %q在打印时用过;%s通常就足够了。
  • 错误输出始终通过进入错误流>&2,因此不会通过管道传递给下游命令
  • 沉默是黄金-除非明确要求,否则不要打印进度信息或调试信息。这使错误更加明显。

1

bash4.2让您测试是否使用-v运算符设置了变量;未设置的变量和设置为空字符串的变量是两个不同的条件:

$ unset foo
$ [[ -v foo ]] && echo foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty
$ foo=
$ [[ -v foo ]] && echo foo is set
foo is set
$ [[ -z "$foo" ]] && echo foo is empty
foo is empty

我在问“多个”变量,所以我在寻找像间接的东西……
Jasper 2014年

你不需要间接的,因为-v名字的变量,所以for var in one two three; [[ -v $var ]] && ...; done将检查如果每个的onetwothree顺序设置。
chepner 2014年

1

我认为,如果您的意思是not set,那么永远不要初始化变量。如果使用[ -n "${!var}" ]two=""则在设置时,like这样的空变量将失败。您可以尝试以下方法:

one=1
three=3

for var in one two three; do
  declare -p $var > /dev/null 2>&1 \
  && printf '%s is set to %s\n' "$var" "${!var}" \
  || printf '%s is not set\n' "$var"
done

0

一个简洁(虽然有点hacky)的解决方案要处理sourced脚本可以将变量设置为空值的情况,可以在采购脚本之前将变量初始化为某个特殊值,然后再检查该值,例如

one=#UNSET#
two=#UNSET#
three=#UNSET#

. set_vars_script

if [[ $one$two$three == *#UNSET#* ]] ; then
  echo "One or more essential variables are unset" >&2
  exit 1
fi

1
另一个不错的解决方案,但我想给出一个更具体的错误消息,然后“未设置一个或多个变量” ...
Jasper 2014年

0

我已经扩展了@mikeserv的答案

此版本使用变量列表来检查其存在,打印缺失变量的名称,并在错误时触发对use()的调用。

REQD_VALUES=("VARIABLE" "FOO" "BAR" "OTHER_VARIABLE")
( i=0; for var_name in ${REQD_VALUES[@]}; do
    VALUE=${!var_name} ;
    i=$((i+1)) ; : ${VALUE:?$var_name is missing}
done ) || usage

缺少值时的示例输出:

./myscript.sh: line 42: VALUE: OTHER_VARIABLE is missing

请注意,您可以将名为VALUE的变量更改为更适合您所需输出的替代名称。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.