小编典典

检查环境变量是否在 Unix shell 脚本中设置的简洁方法是什么?

all

我有一些 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

这是很多打字。是否有更优雅的习惯用法来检查是否设置了一组环境变量?

编辑:我应该提到这些变量没有有意义的默认值 - 如果有任何未设置,脚本应该出错。


阅读 144

收藏
2022-03-11

共1个答案

小编典典

参数扩展

显而易见的答案是使用一种特殊形式的参数扩展:

: ${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。

它通常记录在 shell 手册页中名为Parameter
Expansion
的部分中。例如,bash手册说:

${parameter:?word}

如果为 Null 或未设置,则显示错误。如果 parameter 为 null 或未设置,则 word 的扩展(或如果 word
不存在,则将显示消息)写入标准错误,并且 shell,如果它不是交互式的,则退出。否则,参数的值被替换。

结肠指挥部

我可能应该补充一点,冒号命令只是对其参数进行评估,然后成功。它是原始的 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
$

请注意,当整个表达式不在双引号中时,如何将 in
的值$x先扩展为文件名列表,然后再扩展为文件名列表。*这是shellcheck建议应该解决的问题。我还没有验证它不反对表达式用双引号括起来的形式,但这是一个合理的假设,它会没问题。

2022-03-11