首页 文章

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

提问于
浏览
453

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

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

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

14 回答

  • 21

    这也可以是一种方式:

    if (set -u; : $HOME) 2> /dev/null
    ...
    ...
    

    http://unstableme.blogspot.com/2007/02/checks-whether-envvar-is-set-or-not.html

  • -7

    我一直用:

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

    我担心的不是那么简洁 .

    在CSH下你有$?STATE .

  • 35

    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
    
  • -1
    ${MyVariable:=SomeDefault}
    

    如果 MyVariable 已设置且不为null,则它将重置变量值(=没有任何反应) .
    否则, MyVariable 设置为 SomeDefault .

    以上将尝试执行 ${MyVariable} ,所以如果你只想设置变量do:

    MyVariable=${MyVariable:=SomeDefault}
    
  • 114

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

    #!/bin/sh -u

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

  • 538

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

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

    同样,这是针对/ bin / sh的,并且在旧的Solaris系统上也是兼容的 .

  • 53

    对于像我这样的未来人,我想向前迈出一步并参数化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
    
  • 14

    $? 语法非常简洁:

    if [ $?BLAH == 1 ]; then 
        echo "Exists"; 
    else 
        echo "Does not exist"; 
    fi
    
  • 14

    您的问题取决于您使用的shell .

    伯恩贝壳对你所追求的东西的影响很小 .

    但...

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

    试着远离csh . 与Bourne外壳相比,它增加了它所添加的铃声和口哨声,但它现在真的吱吱作响 . 如果您不相信我,请尝试在csh中分离出STDERR! ( - :

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

    ${MyVariable:=SomeDefault}
    

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

    您还有可能:

    ${MyVariable:-SomeDefault}
    

    它只是将SomeDefault替换为您使用此构造的变量 . 它不会将值SomeDefault赋值给变量,并且在遇到此语句后,MyVariable的值仍为null .

  • 1

    试试这个:

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

    我倾向于加载登录shell中的函数,而不是使用外部shell脚本 . 我使用这样的东西作为辅助函数来检查环境变量而不是任何设置变量:

    is_this_an_env_variable ()
        local var="$1"
        if env |grep -q "^$var"; then
           return 0
        else
           return 1
        fi
     }
    
  • 45

    参数扩展

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

    : ${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 手册说:

    $ {参数:?word}
    如果为空或未设置则显示错误 . 如果参数为null或未设置,则单词的扩展(或者如果单词不存在则为该效果的消息)将写入标准错误,如果不是交互式,则退出shell . 否则,参数的值将被替换 .

    科隆司令部

    我应该补充一点,冒号命令只是对其参数进行评估然后成功 . 这是原始的shell评论符号(在' # ' 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 ' # '评论之前,冒号 Session 被排除在外 . 但是如果你遇到一个以冒号开头的剧本,现在你就会明白为什么 .


    双引号的位置

    blongcomment中询问:

    对此讨论有何想法? https://github.com/koalaman/shellcheck/issues/380#issuecomment-145872749

    讨论的要点是:

    ...但是,当我对它进行shellcheck(版本0.4.1)时,我收到此消息:在script.sh第13行:
    :$ {FOO:?“环境变量'FOO'必须设置且非空”}
    ^ - SC2086:双引号以防止通配和单词分裂 .
    关于在这种情况下我应该做什么的任何建议?

    简短的回答是“按照 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 建议应该修复的内容 . 我没有验证它不反对表达式用双引号括起来的形式,但是它是合理的假设它可以 .

  • 2

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

    #
    # 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
    
  • 1

    上述解决方案都不适用于我的目的,部分原因是我在开始冗长的流程之前检查环境中是否需要设置的开放式变量列表 . 我最终得到了这个:

    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}
    

相关问题