Użycie „$ {a: -b}” do przypisania zmiennych w skryptach

426

Patrzyłem na kilka skryptów, które napisali inni ludzie (szczególnie Red Hat), a wiele ich zmiennych przypisuje się za pomocą poniższej notacji VARIABLE1="${VARIABLE1:-some_val}" lub niektóre rozwijają inne zmienne VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

Po co używać tej notacji zamiast po prostu deklarować wartości bezpośrednio (np. VARIABLE1=some_val)?

Czy są jakieś zalety tej notacji lub możliwe błędy, których można by uniknąć?

Czy :-ma to szczególne znaczenie w tym kontekście?

Rothgar
źródło
Spójrz na man bash; wyszukaj blok „Rozszerzanie parametrów” (około 28%). Te przypisania to np. Funkcje domyślne: „Użyj wartości domyślnej tylko wtedy, gdy nie ustawiono jeszcze żadnej”.
Hauke ​​Laging

Odpowiedzi:

695

Ta technika pozwala na przypisanie zmiennej wartości, jeśli inna zmienna jest pusta lub niezdefiniowana. UWAGA: Ta „inna zmienna” może być tą samą lub inną zmienną.

fragment

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

UWAGA: Formularz ten działa również ${parameter-word}. Jeśli chcesz zobaczyć pełną listę wszystkich form ekspansji parametrów dostępnych w Bash, gorąco polecam przyjrzeć się temu tematowi w wiki Bash Hackera zatytułowanym: „ Rozbudowa parametrów ”.

Przykłady

zmienna nie istnieje
$ echo "$VAR1"

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

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

To samo można zrobić, oceniając inne zmienne lub uruchamiając polecenia w części wartości domyślnej notacji.

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

$

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

Więcej przykładów

Możesz również użyć nieco innej notacji, gdy jest to po prostu VARX=${VARX-<def. value>}.

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

W powyższym $VAR1i $VAR2zostały już zdefiniowane z napisu „ma inną wartość”, ale $VAR3była niezdefiniowana, więc wartość domyślna użyto zamiast 0.

Inny przykład

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

Sprawdzanie i przypisywanie za pomocą :=notacji

Wreszcie będę wspominać operatora poręczne :=. Spowoduje to sprawdzenie i przypisanie wartości, jeśli badana zmienna jest pusta lub niezdefiniowana.

Przykład

Zauważ, że $VAR1teraz jest ustawiony. Operator :=wykonał test i przydział w jednej operacji.

$ unset VAR1
$ echo "$VAR1"

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

Jeśli jednak wartość zostanie ustawiona wcześniej, pozostawia się ją samą.

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

Poręczna tabela referencyjna Dandy

    ss tabeli

Bibliografia

slm
źródło
8
Pamiętaj, że nie wszystkie z nich są udokumentowane w rozszerzeniach bash. Ten ${var:-word}w Q jest, ale nie ${var-word}powyższy. Dokumentacja POSIX ma jednak fajną tabelę, być może warto ją skopiować do tej odpowiedzi - pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Graeme
5
@Graeme, jest to udokumentowane, wystarczy zwrócić uwagę na pominięcie wyników dwukropka w teście tylko dla parametru, który nie jest ustawiony.
Stéphane Chazelas,
7
Używanie echo "${FOO:=default}"jest świetne, jeśli naprawdę chcesz echo. Ale jeśli tego nie zrobisz, wypróbuj :wbudowany ... : ${FOO:=default} Twój $FOOjest ustawiony defaultjak wyżej (tzn. Jeśli nie jest jeszcze ustawiony). Ale $FOOw tym procesie nie ma echa .
fbicknel,
2
Ok, znalazłem odpowiedź: stackoverflow.com/q/24405606/1172302 . Korzystanie z ${4:-$VAR}będzie działać.
Nikos Alexandris,
1
Tutaj masz +1, aby doprowadzić cię do 500. Gratulacje! :)
XtraSimplicity
17

@slm zawiera już dokumenty POSIX - które są bardzo pomocne - ale tak naprawdę nie rozwijają sposobu łączenia tych parametrów, aby wpływać na siebie nawzajem. Nie ma jeszcze żadnej wzmianki o tym formularzu:

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

To jest fragment mojej innej odpowiedzi i myślę, że bardzo dobrze pokazuje, jak działają:

    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?

Kolejny przykład z tego samego :

    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

Powyższy przykład wykorzystuje wszystkie 4 formy podstawiania parametrów POSIX i ich różne :colon nulllub not nulltesty. Więcej informacji znajduje się w powyższym linku, a oto znowu .

Inną rzeczą, o której ludzie często nie myślą, ${parameter:+expansion}jest to, jak bardzo może być przydatna w niniejszym dokumencie. Oto kolejny fragment innej odpowiedzi :

TOP

Tutaj ustawisz niektóre wartości domyślne i przygotujesz się do ich wydrukowania po wywołaniu ...

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

ŚRODKOWY

Tutaj definiujesz inne funkcje, które mają być wywoływane w funkcji drukowania na podstawie ich wyników ...

    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
    }

DOLNY

Masz już wszystko skonfigurowane, więc tutaj wykonasz i wyciągniesz wyniki.

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

WYNIKI

Zaraz się zastanowię, dlaczego, ale uruchomienie powyższego daje następujące wyniki:

_less_important_function()'s pierwszy bieg:

Poszedłem do domu twojej matki i zobaczyłem Disneya na lodzie.

Jeśli wykonasz kaligrafię , odniesiesz sukces.

następnie _more_important_function():

Poszedłem na cmentarz i zobaczyłem Disneya na lodzie.

Jeśli wykonasz matematykę naprawczą , odniesiesz sukces.

_less_important_function() jeszcze raz:

Poszedłem na cmentarz i zobaczyłem Disneya na lodzie.

Jeśli uczysz matematyki naprawczej, będziesz tego żałować.

JAK TO DZIAŁA:

Kluczową cechą tutaj jest koncepcja conditional ${parameter} expansion.Można ustawić zmienną na wartość tylko wtedy, gdy jest ona nieustawiona lub zerowa przy użyciu formularza:

${var_name: =desired_value}

Jeśli zamiast tego chcesz ustawić tylko zmienną nieuzbrojoną, pominiesz, :colona wartości null pozostaną bez zmian.

W ZAKRESIE:

Możesz zauważyć to w powyższym przykładzie $PLACEi $RESULTzmienić się po ustawieniu za pośrednictwem, parameter expansionnawet jeśli _top_of_script_pr()zostało już wywołane, prawdopodobnie ustawiając je po uruchomieniu. Powodem, dla którego to działa, _top_of_script_pr()jest ( subshelled )funkcja - ja ją zawarłem, parensa nie { curly braces }użyłem dla innych. Ponieważ jest wywoływana w podpowłoce, każda ustawiona przez nią zmienna jest, locally scopeda po powrocie do powłoki macierzystej wartości te znikają.

Ale kiedy _more_important_function()ustawia $ACTIONto globally scopedtak wpływa _less_important_function()'sdrugą ocenę $ACTIONponieważ _less_important_function()zestawów $ACTIONtylko poprzez${parameter:=expansion}.

mikeserv
źródło
1
Dobrze widzieć, że
skrócona
8

Osobiste doświadczenie.

Czasem używam tego formatu w swoich skryptach, aby zastępować wartości ad-hoc, np. Jeśli mam:

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

Mogę biec:

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

bez konieczności zmiany oryginalnej wartości domyślnej SOMETHING.

hjk
źródło