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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/307503/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 17:48:00  来源:igfitidea点击:

What's a concise way to check that environment variables are set in a Unix shell script?

bashunixshell

提问by AndrewR

I've got a few Unix shell scripts where I need to check that certain environment variables are set before I start doing stuff, so I do this sort of thing:

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

which is a lot of typing. Is there a more elegant idiom for checking that a set of environment variables is set?

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

EDIT: I should mention that these variables have no meaningful default value - the script should error out if any are unset.

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

回答by Jonathan Leffler

Parameter Expansion

参数扩展

The obvious answer is to use one of the special forms of parameter expansion:

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

: ${STATE?"Need to set STATE"}
: ${DEST:?"Need to set DEST non-empty"}

Or, better (see section on 'Position of double quotes' below):

或者,更好(请参阅下面的“双引号位置”部分):

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

The first variant (using just ?) requires STATE to be set, but STATE="" (an empty string) is OK — not exactly what you want, but the alternative and older notation.

第一个变体(只使用?)需要设置 STATE,但 STATE=""(一个空字符串)是可以的——不完全是你想要的,而是替代和旧的表示法。

The second variant (using :?) requires DEST to be set and non-empty.

第二个变体(使用:?)需要设置 DEST 并且非空。

If you supply no message, the shell provides a default message.

如果您不提供任何消息,shell 将提供一条默认消息。

The ${var?}construct is portable back to Version 7 UNIX and the Bourne Shell (1978 or thereabouts). The ${var:?}construct is slightly more recent: I think it was in System III UNIX circa 1981, but it may have been in PWB UNIX before that. It is therefore in the Korn Shell, and in the POSIX shells, including specifically Bash.

${var?}构造可移植回第 7 版 UNIX 和 Bourne Shell(1978 年左右)。该${var:?}构造稍晚一些:我认为它是在大约 1981 年的 System III UNIX 中,但在此之前它可能已经在 PWB UNIX 中。因此,它在 Korn Shell 和 POSIX shell 中,特别是在 Bash 中。

It is usually documented in the shell's man page in a section called Parameter Expansion. For example, the bashmanual says:

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

${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.

${parameter:?word}

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

The Colon Command

冒号命令

I should probably add that the colon command simply has its arguments evaluated and then succeeds. It is the original shell comment notation (before '#' to end of line). For a long time, Bourne shell scripts had a colon as the first character. The C Shell would read a script and use the first character to determine whether it was for the C Shell (a '#' hash) or the Bourne shell (a ':' colon). Then the kernel got in on the act and added support for '#!/path/to/program' and the Bourne shell got '#' comments, and the colon convention went by the wayside. But if you come across a script that starts with a colon, now you will know why.

我可能应该补充一点,冒号命令只是对其参数进行评估然后成功。它是原始的 shell 注释符号(在 ' #' 到行尾之前)。很长一段时间,Bourne shell 脚本的第一个字符都是冒号。C Shell 将读取脚本并使用第一个字符来确定它是用于 C Shell(一个“ #” 哈希)还是用于 Bourne Shell(一个“ :”冒号)。然后内核开始行动并添加了对 ' #!/path/to/program' 的支持,而 Bourne shell 得到了 ' #' 注释,而冒号约定被搁置了。但是如果你遇到一个以冒号开头的脚本,现在你就会知道为什么了。



Position of double quotes

双引号的位置

blongasked in a comment:

blong评论中问道:

Any thoughts on this discussion? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

对这个讨论有什么想法吗?https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

The gist of the discussion is:

讨论的要点是:

…?However, when I shellcheckit (with version 0.4.1), I get this message:

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?

...?但是,当我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.

在这种情况下我应该怎么做有什么建议吗?

The short answer is "do as shellchecksuggests":

简短的回答是“按shellcheck建议做”:

: "${STATE?Need to set STATE}"
: "${DEST:?Need to set DEST non-empty}"

To illustrate why, study the following. Note that the :command doesn't echo its arguments (but the shell does evaluate the arguments). We want to see the arguments, so the code below uses printf "%s\n"in place of :.

为了说明原因,请研究以下内容。请注意,该:命令不会回显其参数(但 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
$ 

Note how the value in $xis expanded to first *and then a list of file names when the overall expression is not in double quotes. This is what shellcheckis recommending should be fixed. I have not verified that it doesn't object to the form where the expression is enclosed in double quotes, but it is a reasonable assumption that it would be OK.

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

回答by David Schlosnagle

Try this:

尝试这个:

[ -z "$STATE" ] && echo "Need to set STATE" && exit 1;

回答by Rob Wells

Your question is dependent on the shell that you are using.

您的问题取决于您使用的外壳。

Bourne shell leaves very little in the way of what you're after.

Bourne shell 对您所追求的东西几乎没有什么影响。

BUT...

但...

It does work, just about everywhere.

它确实有效,几乎无处不在。

Just try and stay away from csh. It was good for the bells and whistles it added, compared the Bourne shell, but it is really creaking now. If you don't believe me, just try and separate out STDERR in csh! (-:

试着远离csh。与 Bourne 外壳相比,它增加的花里胡哨的效果很好,但现在它真的吱吱作响。如果您不相信我,请尝试在 csh 中分离出 STDERR!(-:

There are two possibilities here. The example above, namely using:

这里有两种可能。上面的例子,即使用:

${MyVariable:=SomeDefault}

for the first time you need to refer to $MyVariable. This takes the env. var MyVariable and, if it is currently not set, assigns the value of SomeDefault to the variable for later use.

第一次需要引用 $MyVariable。这需要环境。var MyVariable,如果当前未设置,则将 SomeDefault 的值分配给变量以供以后使用。

You also have the possibility of:

您还有可能:

${MyVariable:-SomeDefault}

which just substitutes SomeDefault for the variable where you are using this construct. It doesn't assign the value SomeDefault to the variable, and the value of MyVariable will still be null after this statement is encountered.

它只是将 SomeDefault 替换为您使用此构造的变量。它没有给变量赋值 SomeDefault ,遇到这个语句后 MyVariable 的值仍然为空。

回答by Paul Makkar

Surely the simplest approach is to add the -uswitch to the shebang (the line at the top of your script), assuming you're using bash:

当然,最简单的方法是将-u开关添加到shebang(脚本顶部的行),假设您正在使用bash

#!/bin/sh -u

#!/bin/sh -u

This will cause the script to exit if any unbound variables lurk within.

如果任何未绑定的变量潜伏在其中,这将导致脚本退出。

回答by Vincent Van Den Berghe

${MyVariable:=SomeDefault}

If MyVariableis set and not null, it will reset the variable value (= nothing happens).
Else, MyVariableis set to SomeDefault.

如果MyVariable设置并且不为空,它将重置变量值(= 什么也没发生)。
否则,MyVariable设置为SomeDefault

The above will attempt to execute ${MyVariable}, so if you just want to set the variable do:

以上将尝试执行${MyVariable},因此如果您只想设置变量,请执行以下操作:

MyVariable=${MyVariable:=SomeDefault}

回答by Adriano

In my opinion the simplest and most compatible check for #!/bin/shis:

在我看来,对 #!/bin/sh最简单和最兼容的检查是:

if [ "$MYVAR" = "" ]
then
   echo "Does not exist"
else
   echo "Exists"
fi

Again, this is for /bin/sh and is compatible also on old Solaris systems.

同样,这适用于 /bin/sh 并且在旧的 Solaris 系统上也兼容。

回答by chepner

bash4.2 introduced the -voperator which tests if a name is set to anyvalue, even the empty string.

bash4.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

回答by Mr.Ree

I always used:

我一直使用:

if [ "x$STATE" == "x" ]; then echo "Need to set State"; exit 1; fi

Not that much more concise, I'm afraid.

恐怕没有那么简洁。

Under CSH you have $?STATE.

在 CSH 下你有 $?STATE。

回答by nicooga

For future people like me, I wanted to go a step forward and parameterize the var name, so I can loop over a variable sized list of variable names:

对于像我这样的未来人,我想更进一步并参数化 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

回答by codeforester

We can write a nice assertion to check a bunch of variables all at once:

我们可以编写一个很好的断言来一次检查一堆变量:

#
# 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
  [[ "" = "-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
}

Sample invocation:

示例调用:

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

Output:

输出:

test 1: return_code=0
Variable 'three' not set
test 2: return_code=1
Variable 'three' not set


More such assertions here: https://github.com/codeforester/base/blob/master/lib/assertions.sh

这里有更多这样的断言:https: //github.com/codeforester/base/blob/master/lib/assertions.sh