我有一些 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
这是很多打字。是否有更优雅的习惯用法来检查是否设置了一组环境变量?
编辑:我应该提到这些变量没有有意义的默认值 - 如果有任何未设置,脚本应该出错。
显而易见的答案是使用一种特殊形式的参数扩展:
: ${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 且非空。
:?
如果您不提供任何消息,shell 将提供默认消息。
该${var?}构造可移植回版本 7 UNIX 和 Bourne Shell(1978 年左右)。该${var:?}构造稍微更新一些:我认为它在 1981 年左右出现在 System III UNIX 中,但在此之前它可能已经出现在 PWB UNIX 中。因此它存在于 Korn Shell 和 POSIX shell 中,包括特别是 Bash。
${var?}
${var:?}
它通常记录在 shell 手册页中名为Parameter Expansion的部分中。例如,bash手册说:
bash
${parameter:?word} 如果为 Null 或未设置,则显示错误。如果 parameter 为 null 或未设置,则 word 的扩展(或如果 word 不存在,则将显示消息)写入标准错误,并且 shell,如果它不是交互式的,则退出。否则,参数的值被替换。
${parameter:?word}
如果为 Null 或未设置,则显示错误。如果 parameter 为 null 或未设置,则 word 的扩展(或如果 word 不存在,则将显示消息)写入标准错误,并且 shell,如果它不是交互式的,则退出。否则,参数的值被替换。
我可能应该补充一点,冒号命令只是对其参数进行评估,然后成功。它是原始的 shell 注释符号(在 ‘ #‘ 到行尾之前)。很长一段时间以来,Bourne shell 脚本都以冒号作为第一个字符。C Shell 将读取一个脚本并使用第一个字符来确定它是用于 C Shell(一个 ‘ #‘ 散列)还是 Bourne shell(一个 ‘ :‘ 冒号)。然后内核参与进来,增加了对’ #!/path/to/program‘的支持,Bourne shell 得到了’ #‘注释,冒号约定被搁置了。但是如果你遇到一个以冒号开头的脚本,现在你就会知道为什么了。
#
:
#!/path/to/program
对这个讨论有什么想法吗?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(使用版本0.4.1)时,我收到以下消息:
shellcheck
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建议做”:
为了说明原因,请研究以下内容。请注意,该:命令不会回显其参数(但 shell 会评估参数)。我们想查看参数,所以下面的代码使用printf "%s\n".:
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 $
请注意,当整个表达式不在双引号中时,如何将 in 的值$x先扩展为文件名列表,然后再扩展为文件名列表。*这是shellcheck建议应该解决的问题。我还没有验证它不反对表达式用双引号括起来的形式,但这是一个合理的假设,它会没问题。
$x
*