我有一些Unix shell脚本,在开始做事之前,我需要检查某些环境变量是否已设置,因此我要执行以下操作:
if [ -z "$STATE" ]; then
echo "Need to set STATE"
exit 1
fi
if [ -z "$DEST" ]; then
echo "Need to set DEST"
exit 1
fi
这是很多打字。 有没有更优雅的习惯用法来检查是否设置了一组环境变量?
编辑:我应该提到这些变量没有有意义的默认值-如果未设置任何脚本,则脚本应该出错。
#1楼
我认为#!/ bin / sh最简单,最兼容的检查是:
if [ "$MYVAR" = "" ]
then
echo "Does not exist"
else
echo "Exists"
fi
同样,这是针对/ bin / sh的,并且在旧的Solaris系统上也兼容。
#2楼
$?
语法非常简洁:
if [ $?BLAH == 1 ]; then
echo "Exists";
else
echo "Does not exist";
fi
#3楼
${MyVariable:=SomeDefault}
如果设置了MyVariable
并且不为null,它将重置变量值(=什么也没有发生)。
否则, MyVariable
设置为SomeDefault
。
上面将尝试执行${MyVariable}
,因此,如果您只想设置变量,请执行以下操作:
MyVariable=${MyVariable:=SomeDefault}
#4楼
尝试这个:
[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;
#5楼
您的问题取决于您使用的外壳。
伯恩贝壳留下的痕迹很少。
但...
它确实可以在几乎所有地方工作。
只需尝试远离csh。 与Bourne贝壳相比,它增加了铃铛和哨子,但现在确实吱吱作响。 如果您不相信我,只需尝试在csh中分离出STDERR! (-:
这里有两种可能性。 上面的示例,即使用:
${MyVariable:=SomeDefault}
第一次,您需要参考$ MyVariable。 这需要环境。 var MyVariable,如果当前未设置,则将SomeDefault的值分配给变量以供以后使用。
您还可以:
${MyVariable:-SomeDefault}
它只是将SomeDefault替换为您使用此构造的变量。 它不会将值SomeDefault分配给该变量,并且遇到此语句后,MyVariable的值仍将为null。
#6楼
我一直使用:
if [ "x$STATE" == "x" ]; then echo "Need to set State"; exit 1; fi
恐怕没有那么简洁了。
在CSH下,您有$?STATE。
#7楼
参数扩展
显而易见的答案是使用参数扩展的一种特殊形式:
: ${STATE?"Need to set STATE"}
: ${DEST:?"Need to set DEST non-empty"}
或者,更好(请参见下面的“双引号的位置”部分):
: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"
第一个变体(仅使用?
)需要设置STATE,但是STATE =“”(一个空字符串)是可以的—并非完全是您想要的,而是替代的和较旧的表示法。
第二个变体(使用:?
)要求设置DEST且非空。
如果您未提供任何消息,则外壳程序将提供默认消息。
${var?}
构造可移植回版本7 UNIX和Bourne Shell(1978或其前后版本)。 ${var:?}
构造${var:?}
一些:我认为它是在1981年左右的System III UNIX中使用的,但在此之前它可能已经在PWB UNIX中使用了。 因此,它位于Korn Shell和POSIX Shell(特别是Bash)中。
它通常在shell的手册页的Parameter Expansion部分中记录。 例如, bash
手册中说:
${parameter:?word}
如果为空或未设置,则显示错误。 如果parameter为null或未设置,则将单词扩展(或如果单词不存在则显示该信息)写入标准错误,并且退出外壳程序(如果它不是交互式的)。 否则,将替换参数的值。
冒号命令
我可能应该补充一点,冒号命令只是对它的参数求值,然后成功。 它是原始的shell注释符号(在' #
'到行尾)。 长期以来,Bourne shell脚本的首字符是冒号。 C Shell将读取脚本,并使用第一个字符来确定它是用于C Shell(“ #
”哈希)还是Bourne Shell(“ :
”冒号)。 然后内核参与进来,并增加了对“ #!/path/to/program
”的支持,而Bourne shell得到了“ #
”注释,而冒号约定则被抛弃了。 但是,如果遇到以冒号开头的脚本,现在您将知道原因。
双引号的位置
对这个讨论有什么想法吗? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749
讨论的要点是:
…但是,当我
shellcheck
(使用0.4.1版)时,收到以下消息:In script.sh line 13: : ${FOO:?"The environment variable 'FOO' must be set and non-empty"} ^-- SC2086: Double quote to prevent globbing and word splitting.
在这种情况下,我应该怎么做?
简短的答案是“按照shellcheck
建议进行”:
: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"
为了说明原因,请研究以下内容。 注意:
命令不会回显其参数(但是shell会评估参数)。 我们希望看到参数,因此下面的代码使用printf "%s\\n"
代替:
。
$ mkdir junk
$ cd junk
$ > abc
$ > def
$ > ghi
$
$ x="*"
$ printf "%s\n" ${x:?You must set x} # Careless; not recommended
abc
def
ghi
$ unset x
$ printf "%s\n" ${x:?You must set x} # Careless; not recommended
bash: x: You must set x
$ printf "%s\n" "${x:?You must set x}" # Careful: should be used
bash: x: You must set x
$ x="*"
$ printf "%s\n" "${x:?You must set x}" # Careful: should be used
*
$ printf "%s\n" ${x:?"You must set x"} # Not quite careful enough
abc
def
ghi
$ x=
$ printf "%s\n" ${x:?"You must set x"} # Not quite careful enough
bash: x: You must set x
$ unset x
$ printf "%s\n" ${x:?"You must set x"} # Not quite careful enough
bash: x: You must set x
$
请注意,如果整个表达式不在双引号中,则$x
的值将首先扩展为*
,然后扩展为文件名列表。 这是shellcheck
建议的问题,应予以修复。 我尚未验证它是否不反对将表达式用双引号引起来的形式,但这是一个合理的假设,那就是可以。
#8楼
bash
4.2引入了-v
运算符,该运算符测试名称是否设置为任何值,甚至是空字符串。
$ unset a
$ b=
$ c=
$ [[ -v a ]] && echo "a is set"
$ [[ -v b ]] && echo "b is set"
b is set
$ [[ -v c ]] && echo "c is set"
c is set
#9楼
上述解决方案均无法满足我的目的,部分原因是我在开始冗长的过程之前检查了环境中是否需要设置变量的无限制列表。 我结束了这个:
mapfile -t arr < variables.txt
EXITCODE=0
for i in "${arr[@]}"
do
ISSET=$(env | grep ^${i}= | wc -l)
if [ "${ISSET}" = "0" ];
then
EXITCODE=-1
echo "ENV variable $i is required."
fi
done
exit ${EXITCODE}
#10楼
这也可以是一种方法:
if (set -u; : $HOME) 2> /dev/null
...
...
http://unstableme.blogspot.com/2007/02/checks-whether-envvar-is-set-or-not.html
#11楼
我们可以编写一个不错的断言来一次检查所有变量:
#
# assert if variables are set (to a non-empty string)
# if any variable is not set, exit 1 (when -f option is set) or return 1 otherwise
#
# Usage: assert_var_not_null [-f] variable ...
#
function assert_var_not_null() {
local fatal var num_null=0
[[ "$1" = "-f" ]] && { shift; fatal=1; }
for var in "$@"; do
[[ -z "${!var}" ]] &&
printf '%s\n' "Variable '$var' not set" >&2 &&
((num_null++))
done
if ((num_null > 0)); then
[[ "$fatal" ]] && exit 1
return 1
fi
return 0
}
样本调用:
one=1 two=2
assert_var_not_null one two
echo test 1: return_code=$?
assert_var_not_null one two three
echo test 2: return_code=$?
assert_var_not_null -f one two three
echo test 3: return_code=$? # this code shouldn't execute
输出:
test 1: return_code=0
Variable 'three' not set
test 2: return_code=1
Variable 'three' not set
#12楼
对于像我这样的未来人,我想更进一步,并参数化var名称,因此可以遍历可变大小的变量名称列表:
#!/bin/bash
declare -a vars=(NAME GITLAB_URL GITLAB_TOKEN)
for var_name in "${vars[@]}"
do
if [ -z "$(eval "echo \$$var_name")" ]; then
echo "Missing environment variable $var_name"
exit 1
fi
done
#13楼
我倾向于使用登录Shell中的函数而不是使用外部Shell脚本。 我将这样的东西用作帮助函数来检查环境变量,而不是任何设置变量:
is_this_an_env_variable ()
local var="$1"
if env |grep -q "^$var"; then
return 0
else
return 1
fi
}
#14楼
假设您正在使用bash
,最简单的方法肯定是将-u
开关添加到shebang(脚本顶部的行):
#!/bin/sh -u
如果未绑定的变量潜伏其中,这将导致脚本退出。