Usando “$ {a: -b}” para atribuição de variáveis em scripts

334

Eu tenho visto alguns scripts que outras pessoas escreveram (especificamente a Red Hat), e muitas de suas variáveis são atribuídas usando a seguinte notação: %código% ou alguns expandem outras variáveis VARIABLE1="${VARIABLE1:-some_val}"

Qual é o objetivo de usar essa notação em vez de apenas declarar os valores diretamente (por exemplo, VARIABLE2="${VARIABLE2:-'echo $VARIABLE1'}" )?

Existem benefícios para essa notação ou possíveis erros que seriam evitados?

O VARIABLE1=some_val tem um significado específico neste contexto?

    
por Rothgar 03.04.2014 / 02:13

3 respostas

532

Essa técnica permite que uma variável seja atribuída a um valor se outra variável estiver vazia ou indefinida. OBSERVAÇÃO: Essa "outra variável" pode ser a mesma ou outra variável.

trecho

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

NOTA: este formulário também funciona, ${parameter-word} . Se você gostaria de ver uma lista completa de todas as formas de expansão de parâmetros disponíveis no Bash, então eu sugiro que você dê uma olhada neste tópico no wiki do Bash Hacker intitulado: " Expansão do parâmetro ".

Exemplos

variável não existe
$ echo "$VAR1"

$ VAR1="${VAR1:-default value}"
$ echo "$VAR1"
default value
a variável existe
$ VAR1="has value"
$ echo "$VAR1"
has value

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

O mesmo pode ser feito avaliando outras variáveis, ou executando comandos dentro da porção de valor padrão da notação.

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

$

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

Mais exemplos

Você também pode usar uma notação ligeiramente diferente, em que é apenas VARX=${VARX-<def. value>} .

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

No acima, $VAR1 & $VAR2 já foram definidos com a string "tem outro valor", mas $VAR3 foi indefinido, então o valor padrão foi usado, 0 .

Outro exemplo

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

Verificação e atribuição usando := notação

Por fim, mencionarei o prático operador, := . Isso fará uma verificação e atribuirá um valor se a variável em teste estiver vazia ou indefinida.

Exemplo

Observe que $VAR1 está agora definido. O operador := fez o teste e a atribuição em uma única operação.

$ unset VAR1
$ echo "$VAR1"

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

No entanto, se o valor for definido antes, ele será deixado sozinho.

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

Handy Dandy Reference Table

Referências

por 03.04.2014 / 02:39
16

@slm já incluiu os documentos POSIX - que são muito úteis - mas eles não expandem realmente como esses parâmetros podem ser combinados para afetar um ao outro. Ainda não há menção aqui desta forma:

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

Este é um extrato de outra resposta da minha, e acho que demonstra muito bem como estes funcionam:

    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?

Outro exemplo de mesmo :

    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

O exemplo acima aproveita todas as 4 formas de substituição do parâmetro POSIX e seus vários testes :colon null ou not null . Há mais informações no link acima e aqui está novamente .

Outra coisa que as pessoas geralmente não consideram sobre ${parameter:+expansion} é o quão útil pode ser em um documento aqui. Veja outro trecho de uma resposta diferente :

TOP

Aqui você pode definir alguns padrões e se preparar para imprimi-los quando chamado ...

#!/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

MÉDIO

Aqui é onde você define outras funções para chamar sua função de impressão com base em seus resultados ...

    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
    }

FUNDO

Você tem tudo configurado agora, então é aqui que você executará e extrairá seus resultados.

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

RESULTADOS

Vou falar sobre o porquê daqui a pouco, mas a execução acima produz os seguintes resultados:

_less_important_function()'s first run:

I went to your mother's house and saw Disney on Ice.

If you do calligraphy you will succeed.

     

então _more_important_function():

     

I went to the cemetery and saw Disney on Ice.

If you do remedial mathematics you will succeed.

     

_less_important_function() novamente:

     

I went to the cemetery and saw Disney on Ice.

If you do remedial mathematics you will regret it.

COMO FUNCIONA:

A principal característica aqui é o conceito de conditional ${parameter} expansion. Você pode definir uma variável para um valor somente se ela não estiver definida ou nula usando o formulário:

${var_name:=desired_value}

Se, em vez disso, você quiser definir apenas uma variável não definida, omita o :colon e os valores nulos permanecerão como estão.

ON SCOPE:

Você pode notar que no exemplo acima $PLACE e $RESULT são alterados quando definidos via parameter expansion embora < em> _top_of_script_pr() já foi chamado, presumivelmente definindo-os quando é executado. A razão pela qual isso funciona é que _top_of_script_pr() é uma função ( subshelled ) - coloquei em parens em vez de { curly braces } usado para os outros. Como ele é chamado em um subshell, cada variável que ele configura é locally scoped e, quando retorna ao shell pai, esses valores desaparecem.

Mas quando _more_important_function() define $ACTION , é globally scoped , portanto, afeta _less_important_function()'s segunda avaliação de $ACTION porque _less_important_function() define $ACTION somente por ${parameter:=expansion}.

    
por 03.04.2014 / 09:39
7

Experiência pessoal.

Eu uso este formato, às vezes, em meus scripts para fazer ajustes de valores ad-hoc, por exemplo, se eu tiver:

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

Eu posso correr:

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

sem ter que alterar o valor padrão original de SOMETHING .

    
por 03.04.2014 / 08:09