Использование «$ {a: -b}» для назначения переменных в скриптах


427

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

Какой смысл использовать эту запись вместо простого объявления значений (например, VARIABLE1=some_val)?

Есть ли преимущества в этой записи или возможные ошибки, которые можно было бы предотвратить?

Имеет ли :-конкретное значение в этом контексте?


Посмотрите на man bash; поиск по блоку «Расширение параметров» (около 28%). Эти назначения, например, функции по умолчанию: «Используйте значение по умолчанию, только если ни одно еще не было установлено».
Хауке Лагинг

Ответы:


698

Этот метод позволяет переменной присвоить значение, если другая переменная либо пуста, либо не определена. ПРИМЕЧАНИЕ. Эта «другая переменная» может быть той же самой или другой переменной.

выдержка

${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

Справочная таблица Handy Dandy

    сс таблицы

Рекомендации


8
Обратите внимание, что не все из этих расширений описаны в bash. Тот, что ${var:-word}в Q есть, но не ${var-word}выше. В документации по POSIX есть хорошая таблица, хотя, возможно, стоит скопировать ее в этот ответ - pubs.opengroup.org/onlinepubs/9699919799/utilities/…
Грэм,

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
    }

НИЗ

Теперь у вас все настроено, так что вот где вы будете выполнять и извлекать результаты.

    _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.