What's a concise way to check that environment variables are set in a Unix shell script?
我有一些unix shell脚本,在这些脚本中,我需要在开始执行操作之前检查是否设置了某些环境变量,因此我执行以下操作:
1 2 3 4 5 6 7 8 9 | 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 2 | : ${STATE?"Need to set STATE"} : ${DEST:?"Need to set DEST non-empty"} |
或者更好(请参见下面的"双引号位置"部分):
1 2 | :"${STATE?Need to set STATE}" :"${DEST:?Need to set DEST non-empty}" |
第一个变量(仅使用
第二种变体(使用
如果不提供消息,shell将提供默认消息。
它通常记录在shell的手册页中的一个称为参数扩展的部分中。例如,
1 ${parameter:?word}Display Error if Null or Unset. If parameter is null or unset, the expansion of word (or a message to that effect if word is not present) is written to the standard error and the shell, if it is not interactive, exits. Otherwise, the value of parameter is substituted.
colon命令
我可能应该补充一点,冒号命令只是计算了它的参数,然后成功了。它是原始的shell注释符号(在'
Blong在评论中问道:
Any thoughts on this discussion? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749
讨论的要点是:
… However, when I
shellcheck it (with version 0.4.1), I get this message:
1
2
3 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.Any advice on what I should do in this case?
简短的回答是"照
1 2 | :"${STATE?Need to set STATE}" :"${DEST:?Need to set DEST non-empty}" |
为了说明原因,请学习以下内容。请注意,
"
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 | $ mkdir junk $ cd junk $ > abc $ > def $ > ghi $ $ x="*" $ printf"%s " ${x:?You must set x} # Careless; not recommended abc def ghi $ unset x $ printf"%s " ${x:?You must set x} # Careless; not recommended bash: x: You must set x $ printf"%s ""${x:?You must set x}" # Careful: should be used bash: x: You must set x $ x="*" $ printf"%s ""${x:?You must set x}" # Careful: should be used * $ printf"%s " ${x:?"You must set x"} # Not quite careful enough abc def ghi $ x= $ printf"%s " ${x:?"You must set x"} # Not quite careful enough bash: x: You must set x $ unset x $ printf"%s " ${x:?"You must set x"} # Not quite careful enough bash: x: You must set x $ |
请注意,当整个表达式不是双引号时,
试试这个:
1 | [ -z"$STATE" ] && echo"Need to set STATE" && exit 1; |
你的问题取决于你使用的外壳。
BourneShell对你的追求几乎没有影响。
但是…
它确实有效,几乎无处不在。
试着远离CSH。与波恩贝壳相比,它对钟和口哨很有好处,但现在它真的吱吱作响。如果你不相信我,试着在CSH中分离出stderr!(-)
这里有两种可能性。上面的示例,即使用:
1 | ${MyVariable:=SomeDefault} |
第一次需要引用$myvariable。这需要环境。var myvariable,如果当前未设置,则将somedefault的值分配给该变量供以后使用。
您还可以:
1 | ${MyVariable:-SomeDefault} |
它只是用某个默认值替换您正在使用此构造的变量。它不会将值somedefault赋给变量,遇到此语句后,myvariable的值仍然为空。
当然,最简单的方法是将
如果有任何未绑定的变量隐藏在脚本中,这将导致脚本退出。
1 | ${MyVariable:=SomeDefault} |
如果设置了
上面将尝试执行
1 | MyVariable=${MyVariable:=SomeDefault} |
在我看来,最简单和最兼容的检查!/BI/SH为:
1 2 3 4 5 6 | if ["$MYVAR" ="" ] then echo"Does not exist" else echo"Exists" fi |
同样,这是针对/bin/sh的,并且在旧的Solaris系统上也兼容。
1 2 3 4 5 6 7 8 | $ 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 |
我经常使用:
1 | if ["x$STATE" =="x" ]; then echo"Need to set State"; exit 1; fi |
恐怕没那么简洁。
在CSH下你有美元?状态。
对于像我这样的未来人,我想向前迈出一步,参数化var名称,这样我就可以循环访问变量大小的变量名称列表:
1 2 3 4 5 6 7 8 9 10 | #!/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 |
我们可以编写一个很好的断言来同时检查一系列变量:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | # # 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 '"Variable '$var' not set">&2 && ((num_null++)) done if ((num_null > 0)); then [["$fatal" ]] && exit 1 return 1 fi return 0 } |
示例调用:
1 2 3 4 5 6 7 | 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 |
输出:
1 2 3 4 | test 1: return_code=0 Variable 'three' not set test 2: return_code=1 Variable 'three' not set |
以上所有解决方案都不能满足我的目的,部分原因是我在环境中检查了一个开放的变量列表,这些变量在开始一个冗长的过程之前需要设置。最后我得到了:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 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} |
这也是一种方式:
1 2 3 | if (set -u; : $HOME) 2> /dev/null ... ... |
http://unstableme.blogspot.com/2007/02/checks-when-envvar-is-set-or-not.html
我倾向于在登录shell中加载函数,而不是使用外部shell脚本。我使用类似这样的辅助函数来检查环境变量,而不是任何设置变量:
1 2 3 4 5 6 7 8 | is_this_an_env_variable () local var="$1" if env |grep -q"^$var"; then return 0 else return 1 fi } |
1 2 3 4 5 | if [ $?BLAH == 1 ]; then echo"Exists"; else echo"Does not exist"; fi |