在脚本中使用“${a:-b}”进行变量赋值

在脚本中使用“${a:-b}”进行变量赋值

我一直在查看其他人编写的一些脚本(特别是Red Hat),并且他们的很多变量都是使用以下符号 VARIABLE1="${VARIABLE1:-some_val}" 或一些扩展其他变量 来分配的VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

使用此表示法而不是直接声明值(例如,)有什么意义VARIABLE1=some_val

这种表示法有什么好处或者可以避免可能出现的错误吗?

:-在这种情况下有特定的含义吗?

答案1

如果另一个变量为空或未定义,此技术允许为一个变量分配一个值。笔记:这个“其他变量”可以是相同的或另一个变量。

摘抄

${parameter:-word}
    If parameter is unset or null, the expansion of word is substituted. 
    Otherwise, the value of parameter is substituted.

笔记:这种形式也有效,${parameter-word}.根据Bash 文档,对于所有此类扩展:

省略冒号会导致仅测试未设置的参数。换句话说,如果包含冒号,则操作员会测试两者范围的存在并且其值不为空;如果省略冒号,则运算符仅测试是否存在。

如果您想查看 Bash 中可用的所有形式的参数扩展的完整列表,那么我强烈建议您查看 Bash Hacker 的 wiki 中的这个主题,标题为:“参数扩展”。

例子

变量不存在

$ echo "$VAR1"

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
default value

变量存在

$ VAR1="has value"
$ echo "$VAR1"
has value

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
has value

可以通过评估其他变量或在符号的默认值部分内运行命令来完成相同的操作。

$ VAR2="has another value"
$ echo "$VAR2"
has another value
$ echo "$VAR1"

$

$ VAR1="${VAR1:-$VAR2}"
$ echo "$VAR1"
has another value

更多示例

您还可以使用稍微不同的表示法,即VARX=${VARX-<def. value>}.

$ echo "${VAR1-0}"
has another value
$ echo "${VAR2-0}"
has another value
$ echo "${VAR3-0}"
0

在上面的$VAR1&中$VAR2,已经用字符串“has another value”定义了,但$VAR3未定义,因此使用默认值0.

另一个例子

$ VARX="${VAR3-0}"
$ echo "$VARX"
0

使用:=符号检查和分配

最后我要提一下方便的操作符,:=。如果被测试的变量为空或未定义,这将进行检查并分配一个值。

例子

请注意,$VAR1现在已设置。操作员:=在一次操作中完成了测试和分配。

$ unset VAR1
$ echo "$VAR1"

$ echo "${VAR1:=default}"
default
$ echo "$VAR1"
default

然而,如果该值是事先设置的,那么它就保持不变。

$ VAR1="some value"
$ echo "${VAR1:=default}"
some value
$ echo "$VAR1"
some value

方便的花花公子参考表

参数设置且不为空 参数已设置但为空 参数未设置
${parameter:-word} 代替范围 代替单词 代替单词
${parameter-word} 代替范围 代替无效的 代替单词
${parameter:=word} 代替范围 分配单词 分配单词
${parameter=word} 代替范围 代替无效的 分配单词
${parameter:?word} 代替范围 错误,退出 错误,退出
${parameter?word} 代替范围 代替无效的 错误,退出
${parameter:+word} 代替单词 代替无效的 代替无效的
${parameter+word} 代替单词 代替单词 代替无效的

源表截图

之间存在差异任务代换显式:赋值为变量设置值,而替换则不然。

参考

答案2

@slm 已经包含了POSIX 文档- 这非常有帮助 - 但它们并没有真正扩展这些参数如何组合以相互影响。这里还没有提到这种形式:

${var?if unset parent shell dies and this message is output to stderr}

这是摘录自另一个答案我的,我认为它很好地展示了它们是如何工作的:

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

另一个例子来自相同的:

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

上面的示例利用了 POSIX 参数替换的所有 4 种形式及其各种形式:colon null或者not null测试。上面的链接中有更多信息,并且又来了

人们经常没有考虑到的另一件事${parameter:+expansion}是它在此处文档中的作用有多大。这是另一段摘录不同的答案:

顶部

在这里您将设置一些默认值并准备在调用时打印它们......

#!/bin/sh
    _top_of_script_pr() ( 
        IFS="$nl" ; set -f #only split at newlines and don't expand paths
        printf %s\\n ${strings}
    ) 3<<-TEMPLATES
        ${nl=
}
        ${PLACE:="your mother's house"}
        ${EVENT:="the unspeakable."}
        ${ACTION:="heroin"}
        ${RESULT:="succeed."}
        ${strings:="
            I went to ${PLACE} and saw ${EVENT}
            If you do ${ACTION} you will ${RESULT}
        "}
    #END
    TEMPLATES

中间

您可以在此处定义其他函数,以根据其结果调用打印函数...

    EVENT="Disney on Ice."
    _more_important_function() { #...some logic...
        [ $((1+one)) -ne 2 ] && ACTION="remedial mathematics"
            _top_of_script_pr
    }
    _less_important_function() { #...more logic...
        one=2
        : "${ACTION:="calligraphy"}"
        _top_of_script_pr
    }

底部

现在您已经完成了所有设置,因此您将在此处执行并提取结果。

    _less_important_function
    : "${PLACE:="the cemetery"}" 
    _more_important_function
    : "${RESULT:="regret it."}" 
    _less_important_function    

结果

我稍后会解释原因,但运行上面的命令会产生以下结果:

_less_important_function()'s第一次运行:

我去你妈妈家看到冰上迪士尼。

如果你这样做书法你会成功的。

然后_more_important_function():

我去了公墓还看了冰上迪士尼。

如果你这样做补习数学你会成功的。

_less_important_function()再次:

我去了墓地,看了冰上迪士尼。

如果你补习数学,你会后悔。

怎么运行的:

这里的关键特征是概念conditional ${parameter} expansion.仅当变量未设置或为 null 时,才可以使用以下形式将变量设置为值:

${var_name:=desired_value}

如果您只想设置一个未设置的变量,则可以省略:colon并且空值将保持原样。

范围:

您可能会注意到,在上面的示例中$PLACE$RESULT通过设置时会发生变化parameter expansion虽然_top_of_script_pr()已经被调用,大概在运行时设置它们。这有效的原因是_top_of_script_pr()是一个( subshelled )函数 - 我将其包含在parens而不是{ curly braces }用于其他人。因为它是在子 shell 中调用的,所以它设置的每个变量都是locally scoped当它返回到其父 shell 时,这些值就会消失。

但当_more_important_function()$ACTION这是globally scoped所以它影响_less_important_function()'s第二次评价$ACTION因为_less_important_function()$ACTION仅通过${parameter:=expansion}.

答案3

个人经验。

我有时在脚本中使用这种格式来临时覆盖值,例如,如果我有:

$ cat script.sh
SOMETHING="${SOMETHING:-something}"; echo "$SOMETHING"; 

我可以跑:

$ env SOMETHING="something other than the default value" ./script.sh` 

无需更改 的原始默认值SOMETHING

答案4

获取单个命令行参数的一种有趣方法是使用 $1, $2 ... 方案。

echo "$1 $2 $3"
Two=$(2:-'default2')
echo "Second parameter: $Two"

带参数调用:赢得了树

结果:第二个参数:太

调用参数:五个!

结果:第二个参数:default2

相关内容