Utilizzo di "$ {a: -b}" per l'assegnazione di variabili negli script


427

Ho esaminato alcuni script che altre persone hanno scritto (in particolare Red Hat) e molte delle loro variabili sono assegnate usando la seguente notazione VARIABLE1="${VARIABLE1:-some_val}" o alcune espandono altre variabili VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

Qual è lo scopo di usare questa notazione invece di dichiarare direttamente i valori (ad es. VARIABLE1=some_val)?

Ci sono vantaggi a questa notazione o possibili errori che sarebbero evitati?

Ha :-un significato specifico in questo contesto?


Dai un'occhiata a man bash; cercare il blocco "Parameter Expansion" (circa il 28%). Queste assegnazioni sono ad esempio funzioni predefinite: "Utilizzare il valore predefinito solo se non è stato ancora impostato nessuno".
Hauke ​​Laging,

Risposte:


698

Questa tecnica consente di assegnare un valore a una variabile se un'altra variabile è vuota o non è definita. NOTA: questa "altra variabile" può essere la stessa o un'altra variabile.

estratto

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

NOTA: Questa forma funziona anche, ${parameter-word}. Se desideri vedere un elenco completo di tutte le forme di espansione dei parametri disponibili in Bash, ti consiglio vivamente di dare un'occhiata a questo argomento nel wiki di Bash Hacker dal titolo: " Espansione dei parametri ".

Esempi

la variabile non esiste
$ echo "$VAR1"

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

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

La stessa cosa può essere fatta valutando altre variabili o eseguendo comandi all'interno della parte del valore predefinito della notazione.

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

$

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

Altri esempi

Puoi anche usare una notazione leggermente diversa in cui è solo VARX=${VARX-<def. value>}.

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

In quanto sopra $VAR1& $VAR2erano già definiti con la stringa "ha un altro valore" ma $VAR3non era definito, quindi è stato utilizzato il valore predefinito, invece 0.

Un altro esempio

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

Verifica e assegnazione tramite :=notazione

Infine citerò l'operatore a portata di mano, :=. Questo farà un controllo e assegnerà un valore se la variabile sotto test è vuota o indefinita.

Esempio

Si noti che $VAR1è ora impostato. L'operatore ha :=eseguito il test e l'assegnazione in un'unica operazione.

$ unset VAR1
$ echo "$VAR1"

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

Tuttavia, se il valore è impostato in precedenza, viene lasciato solo.

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

Tabella di riferimento di Handy Dandy

    ss della tabella

Riferimenti


8
Si noti che non tutte queste espansioni sono documentate in bash. L' ${var:-word}uno in Q è, ma non il ${var-word}sopra. La documentazione POSIX ha una bella tabella, ma potrebbe valerne la
Graeme,

5
@Graeme, è documentato, devi solo prestare attenzione a Omettere i due punti in un test solo per un parametro non impostato.
Stéphane Chazelas,

7
L'utilizzo echo "${FOO:=default}"è ottimo se si desidera effettivamente echo. Ma se non lo fai, prova il :built-in ... : ${FOO:=default} Il tuo $FOOè impostato defaultcome sopra (cioè, se non è già impostato). Ma non c'è eco $FOOnel processo.
fbicknel,

2
Ok, ho trovato una risposta: stackoverflow.com/q/24405606/1172302 . Utilizzando il ${4:-$VAR}funzionerà.
Nikos Alexandris,

1
Ecco, hai un +1 per portarti a 500. Complimenti! :)
XtraSimplicity,

17

@slm ha già incluso i documenti POSIX - che sono molto utili - ma in realtà non si espandono sul modo in cui questi parametri possono essere combinati per influenzarsi a vicenda. Non c'è ancora alcuna menzione qui di questo modulo:

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

Questo è un estratto di un'altra mia risposta , e penso che dimostri molto bene come funzionano:

    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?

Un altro esempio dallo stesso :

    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

L'esempio precedente sfrutta tutte e 4 le forme di sostituzione dei parametri POSIX e i loro vari :colon nullo not nulltest. Ci sono maggiori informazioni nel link sopra, ed eccolo di nuovo .

Un'altra cosa che la gente spesso non considera ${parameter:+expansion}è quanto possa essere utile in un documento qui. Ecco un altro estratto da una risposta diversa :

SUPERIORE

Qui imposterai alcune impostazioni predefinite e ti preparerai a stamparle quando viene chiamato ...

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

MEDIO

Qui è dove si definiscono altre funzioni da chiamare sulla funzione di stampa in base ai loro risultati ...

    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
    }

PARTE INFERIORE

Ora hai tutto configurato, quindi ecco dove eseguirai e tirerai i risultati.

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

RISULTATI

Esaminerò il perché tra un momento, ma l'esecuzione di quanto sopra produce i seguenti risultati:

_less_important_function()'s prima corsa:

Sono andato a casa di tua madre e ho visto Disney on Ice.

Se fai calligrafia avrai successo.

poi _more_important_function():

Sono andato al cimitero e ho visto Disney on Ice.

Se fai matematica correttiva ci riuscirai.

_less_important_function() ancora:

Sono andato al cimitero e ho visto Disney on Ice.

Se fai matematica correttiva te ne pentirai.

COME FUNZIONA:

La caratteristica chiave qui è il concetto di conditional ${parameter} expansion.È possibile impostare una variabile su un valore solo se non è impostata o è nulla utilizzando il modulo:

${var_name: =desired_value}

Se invece desideri impostare solo una variabile non impostata, ometteresti i :colonvalori null e rimarrebbero così come sono.

IN CAMPO DI APPLICAZIONE:

Potresti notare che nell'esempio sopra $PLACEe $RESULTessere cambiato quando impostato tramite parameter expansionanche se _top_of_script_pr()è già stato chiamato, presumibilmente impostandoli quando viene eseguito. Il motivo per cui funziona è che _top_of_script_pr()è una ( subshelled )funzione: l'ho racchiusa in un oggetto parensanziché { curly braces }nell'altro. Poiché viene chiamato in una subshell, ogni variabile che imposta è locally scopede quando ritorna alla sua shell padre quei valori scompaiono.

Ma quando _more_important_function()imposta $ACTIONè globally scopedcosì influenza la _less_important_function()'sseconda valutazione di $ACTIONperché _less_important_function()imposta $ACTIONsolo tramite${parameter:=expansion}.


1
È bello vedere che il valore predefinito abbreviato funziona in Bourne Shell
Sridhar Sarnobat,

8

Esperienza personale.

Uso questo formato a volte nei miei script per eseguire il superamento dei valori ad hoc, ad esempio se ho:

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

Posso correre:

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

senza dover modificare il valore predefinito originale di SOMETHING.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.