Використання "$ {a: -b}" для призначення змінних у скриптах


426

Я переглянув кілька сценаріїв, які писали інші люди (зокрема Red Hat), і багато їх змінних присвоюються за допомогою наступних позначень VARIABLE1="${VARIABLE1:-some_val}" або деяких розширень інших змінних VARIABLE2="${VARIABLE2:-`echo $VARIABLE1`}"

Який сенс використовувати цю позначення, а не просто оголошувати значення безпосередньо (наприклад, VARIABLE1=some_val)?

Чи є корисні для цього позначення або можливі помилки, які могли б бути попереджені?

Чи :-має конкретне значення в цьому контексті?


Погляньте man bash; пошук блоку "Розширення параметрів" (приблизно на 28%). Це призначення, наприклад, функції за замовчуванням: "Використовуйте значення за замовчуванням, тільки якщо жодне ще не встановлено."
Hauke ​​Laging

Відповіді:


695

Ця методика дозволяє змінній призначити значення, якщо інша змінна або порожня, або невизначена. ПРИМІТКА. Ця "інша змінна" може бути тією ж чи іншою змінною.

витяг

${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 Hacker під назвою: " Розширення параметрів ".

Приклади

змінна не існує
$ 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вже були визначені рядком «має інше значення» , але $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

Справочний стіл "Денді"

    ss таблиці

Список літератури


8
Зауважте, що не всі ці розширення задокументовані в bash. Той, що ${var:-word}в Q є, але не ${var-word}вище. У документації POSIX є приємна таблиця, але, можливо, варто скопіювати її у цю відповідь - pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Graeme

5
@Graeme, це задокументовано, вам просто потрібно звернути увагу на те, що опущення результатів товстої кишки є тестом лише для параметра, який не встановлений.
Стефан Шазелас

7
Використовувати echo "${FOO:=default}"чудово, якщо ви насправді хочете echo. Якщо ви цього не зробите, спробуйте :вбудований ... : ${FOO:=default} Ваш параметр $FOOвстановлений defaultяк вище (тобто, якщо він уже не встановлений) Але відлуння $FOOв цьому процесі немає.
fbicknel

2
Гаразд, знайшов відповідь: stackoverflow.com/q/24405606/1172302 . Використання ${4:-$VAR}заповіту спрацює.
Нікос Олександріс

1
Тут є +1, щоб привести вас до 500. Поздоровлення! :)
XtraSimplicity

17

@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

У наведеному вище прикладі використовуються всі 4 форми заміни параметрів POSIX та їх різні :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
    }

BOTTOM

Зараз у вас все налаштовано, тому ось, де ви будете виконувати та виводити свої результати.

    _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.Ви можете встановити змінну до значення лише у тому випадку, якщо вона не встановлена ​​чи недійсна за допомогою форми:

${var_name: =desired_value}

Якщо замість цього ви хочете встановити лише незмінену змінну, ви опустите значення :colonта null, які залишаться такими, які є.

НА ОБЛАСТІ:

Ви можете помітити , що в наведеному вище прикладі $PLACEі $RESULTпереодягатися при установці з допомогою , parameter expansionнавіть якщо _top_of_script_pr()вже були викликані, ймовірно , встановивши їх , коли він запущений. Причина цього працює в тому, що _top_of_script_pr()це ( subshelled )функція - я вклав її, parensа не { curly braces }використовував для інших. Оскільки він викликається в підклітині, кожна змінна, яку він встановить, є, locally scopedі коли вона повертається до батьківської оболонки, ці значення зникають.

Але коли _more_important_function()встановлено $ACTIONце globally scopedтак, це впливає на _less_important_function()'sдругу оцінку, $ACTIONоскільки _less_important_function()встановлює $ACTIONлише через${parameter:=expansion}.



8

Особистий досвід.

Я використовую цей формат іноді в своїх сценаріях, щоб виконувати спеціальне перевищення значень, наприклад, якщо у мене є:

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

Я можу бігати:

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

без необхідності змінювати початкове значення за замовчуванням SOMETHING.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.