Utilisation de «$ {a: -b}» pour l'affectation de variable dans les scripts

429

J'ai consulté quelques scripts écrits par d'autres personnes (en particulier Red Hat), et bon nombre de leurs variables sont affectées à l'aide de la notation suivante VARIABLE1="${VARIABLE1:-some_val}" ou développent d'autres variables. VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

Quel est l'intérêt d'utiliser cette notation au lieu de simplement déclarer les valeurs directement (par exemple, VARIABLE1=some_val)?

Y at-il des avantages à cette notation ou des erreurs possibles qui pourraient être évitées?

Le a-t- :-il une signification spécifique dans ce contexte?

Rothgar
la source
Jetez un oeil à man bash; recherchez le bloc "Parameter Expansion" (environ 28%). Ces assignations sont par exemple des fonctions par défaut: "Utilisez la valeur par défaut uniquement si aucune n'a encore été définie."
Hauke ​​Laging

Réponses:

701

Cette technique permet d'attribuer une valeur à une variable si une autre variable est vide ou non définie. Remarque: cette "autre variable" peut être la même ou une autre variable.

extrait

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

REMARQUE: Ce formulaire fonctionne également ${parameter-word}. Si vous souhaitez voir une liste complète de toutes les formes d'expansion de paramètres disponibles dans Bash, je vous suggère vivement de jeter un coup d'œil à cette rubrique dans le wiki de Bash Hacker intitulé " Expansion des paramètres ".

Exemples

la variable n'existe pas
$ echo "$VAR1"

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

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

La même chose peut être faite en évaluant d'autres variables ou en exécutant des commandes dans la partie valeur par défaut de la notation.

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

$

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

Plus d'exemples

Vous pouvez également utiliser une notation légèrement différente là où c'est juste VARX=${VARX-<def. value>}.

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

Dans ce qui précède $VAR1et $VAR2déjà défini par la chaîne « a une autre valeur » , mais $VAR3était définie, la valeur par défaut a été utilisé à la place, 0.

Un autre exemple

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

Vérifier et assigner en utilisant la :=notation

Enfin , je mentionnerai l'opérateur à portée de main, :=. Cela fera une vérification et assignera une valeur si la variable à tester est vide ou non définie.

Exemple

Notez que $VAR1c'est maintenant réglé. L’opérateur a :=fait le test et l’affectation en une seule opération.

$ unset VAR1
$ echo "$VAR1"

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

Cependant, si la valeur est définie auparavant, elle est laissée seule.

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

Table de référence Handy Dandy

    ss de table

Références

slm
la source
8
Notez que toutes ces extensions ne sont pas documentées dans bash. Celui ${var:-word}du Q est, mais pas celui ${var-word}ci - dessus. La documentation POSIX a un joli tableau, mais il vaut peut-être la peine de la copier dans cette réponse - pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Graeme le
5
@Graeme, cela est documenté, il vous suffit de faire attention aux résultats suivants
Stéphane Chazelas
7
Utiliser echo "${FOO:=default}"est génial si vous voulez réellement le echo. Mais si vous ne le faites pas, essayez la fonction :intégrée ... : ${FOO:=default} Votre $FOOconfiguration est la suivante default(c’est-à-dire si elle n’a pas déjà été définie). Mais il n'y a pas d'écho $FOOdans le processus.
fbicknel
2
Ok, a trouvé une réponse: stackoverflow.com/q/24405606/1172302 . Utiliser le ${4:-$VAR}travail va.
Nikos Alexandris
1
Ici, un +1 pour vous amener à 500. Félicitations! :)
XtraSimplicity
17

@slm a déjà inclus les documents POSIX - qui sont très utiles - mais ils ne développent pas vraiment la manière dont ces paramètres peuvent être combinés pour s’affecter mutuellement. Il n'y a pas encore de mention ici de ce formulaire:

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

Ceci est un extrait d’ une autre de mes réponses , et je pense que cela montre très bien comment cela fonctionne:

    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 autre exemple de même :

    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'exemple ci - dessus profite de toutes les 4 formes de substitution de paramètres et de leurs différents POSIX :colon nullou not nulltests. Il y a plus d'informations dans le lien ci-dessus, et le voici à nouveau .

Une autre chose à laquelle les gens ne pensent souvent pas, ${parameter:+expansion}c'est à quel point cela peut être utile dans un document ici. Voici un autre extrait d'une réponse différente :

HAUT

Ici, vous définissez des valeurs par défaut et vous préparez à les imprimer lorsque vous êtes appelé ...

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

MILIEU

C'est ici que vous définissez d'autres fonctions pour appeler votre fonction d'impression en fonction de leurs résultats ...

    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
    }

BAS

Vous avez tout configuré maintenant, alors voici où vous allez exécuter et extraire vos résultats.

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

RÉSULTATS

J'expliquerai pourquoi dans un instant, mais l'exécution de ce qui précède produit les résultats suivants:

_less_important_function()'s première exécution:

Je suis allé chez votre mère et j'ai vu Disney sur glace.

Si vous faites la calligraphie, vous réussirez.

ensuite _more_important_function():

Je suis allé au cimetière et j'ai vu Disney sur glace.

Si vous faites des mathématiques correctives, vous réussirez.

_less_important_function() encore:

Je suis allé au cimetière et j'ai vu Disney sur glace.

Si vous faites des mathématiques correctives, vous le regretterez.

COMMENT ÇA FONCTIONNE:

La caractéristique clé ici est le concept de conditional ${parameter} expansion.Vous pouvez définir une variable sur une valeur uniquement si elle est non définie ou nulle en utilisant le formulaire:

${var_name: =desired_value}

Si, au lieu de cela, vous souhaitez définir uniquement une variable non définie, vous omettez les :colonvaleurs et null resteraient telles quelles.

SUR LA PORTÉE:

Vous remarquerez peut-être que, dans l'exemple ci-dessus $PLACE, les $RESULTmodifications sont apportées lorsqu'elles sont définies via parameter expansionmême si elles _top_of_script_pr()ont déjà été appelées, probablement en les configurant lors de leur exécution. La raison pour laquelle cela fonctionne est que _top_of_script_pr()c'est une ( subshelled )fonction - je l'ai incluse parensplutôt que celle { curly braces }utilisée pour les autres. Comme elle est appelée dans un sous-shell, toutes les variables qu'elle définit sont locally scopedet, à mesure qu'elles retournent dans leur shell parent, ces valeurs disparaissent.

Mais lorsqu’il est _more_important_function()défini, $ACTIONil en va de même globally scopedpour la _less_important_function()'sdeuxième évaluation $ACTIONcar _less_important_function(), $ACTIONuniquement via${parameter:=expansion}.

Mikeserv
la source
1
Il est bon de voir que la valeur par défaut abrégée fonctionne dans Bourne Shell
Sridhar Sarnobat
8

Expérience personnelle.

J'utilise parfois ce format dans mes scripts pour minimiser les valeurs ad-hoc, par exemple si j'ai:

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

Je peux courir:

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

sans avoir à changer la valeur par défaut d'origine de SOMETHING.

hjk
la source