Использование unset против установки переменной на пустую

108

В настоящее время я пишу структуру тестирования bash, где в тестовой функции могут использоваться как стандартные тесты bash ( [[), так и предопределенные сопоставители. Сопоставители являются оболочками для '[[' и помимо возврата кода возврата, устанавливают какое-то значимое сообщение, говорящее о том, что ожидалось.

Пример:

string_equals() {
    if [[ ! $1 = $2 ]]; then
            error_message="Expected '$1' to be '$2'."

            return 1
    fi
}

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

Теперь, в какой-то момент позже, я проверяю, прошли ли тесты успешно. Если это удалось, я печатаю ожидание зеленым, если не удалось - красным.

Кроме того, может быть установлен error_message, поэтому я проверяю, существует ли сообщение, распечатываю его и затем снимаю его (потому что следующий тест может не установить error_message):

if [[ $error_message ]]; then
    printf '%s\n' "$error_message"

    unset -v error_message
fi

Теперь мой вопрос: лучше ли отключить переменную или просто установить ее на '', например

error_message=''

Какой лучше? Действительно ли это имеет значение? Или, может быть, мне стоит установить дополнительный флаг, указывающий, что сообщение было установлено?

вспомогательный метод
источник
1
Если никогда не сравнивать ни error_messageс чем, я бы сказал, это не имеет значения. Однако я думаю, что вы хотите [[ $error_message ]], иначе вы проверяете, существует ли буквальная строка error_message.
chepner
@chepner Да, это была опечатка. Починил это.
helpermethod

Ответы:

143

Обычно вы не видите разницы, если не используете set -u:

/home/user1> var=""
/home/user1> echo $var

/home/user1> set -u
/home/user1> echo $var

/home/user1> unset var
/home/user1> echo $var
-bash: var: unbound variable

На самом деле, это зависит от того, как вы собираетесь тестировать переменную.

Я добавлю, что мой предпочтительный способ тестирования, если он установлен:

[[ -n $var ]]  # True if the length of $var is non-zero

или

[[ -z $var ]]  # True if zero length
cdarke
источник
43
var=не "не установлено". Это просто пустая строка без кавычек. В дополнение к set -uразличным формам расширения параметров в bash можно также различать значения unset и null: ${foo:bar}расширяется до bar, когда fooне установлено, но "", когда fooимеет значение null, а ${foo:-bar}расширяется до bar, если foo не установлено или равно null.
chepner
1
[[ -n $var ]]ложно, если varзадана пустая строка.
chepner
1
если вы используете 'declare -p' для проверки существования переменной, тогда var = покажет 'declare - var = ""', вы должны использовать unset, чтобы избавиться от него, конечно, если это переменная только для чтения, вы не можете получить избавиться от этого конечно. Кроме того, если вы используете var = something внутри функции, вам не придется беспокоиться об избавлении от этого, если вы используете «local var = value», но будьте осторожны, потому что «declare var = value» также является локальным. вы должны явно установить его как глобальный, используя «declare -g var = value», не объявляя, что вы должны явно установить его как локальный, используя «local». Глобальные переменные удаляются только / w unset.
osirisgothra
@osirisgothra: хорошее замечание о локальных переменных. Конечно, обычно лучше всего объявить (или локализовать) все переменные в функции, чтобы она была инкапсулирована.
cdarke 07
3
@chepner должен быть ${foo-bar}вместо ${foo:bar}. unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
проверь
17

Как уже было сказано, использование unset отличается и от массивов

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2
Стивен Пенни
источник
2

Таким образом, отключив индекс массива 2, вы по существу удаляете этот элемент в массиве и уменьшаете размер массива (?).

Я сделал свой тест ..

foo=(5 6 8)
echo ${#foo[*]}
unset foo
echo ${#foo[*]}

В результате ..

3
0

Итак, просто чтобы уточнить, что отключение всего массива фактически полностью удалит его.

PdC
источник
0

Основываясь на комментариях выше, вот простой тест:

isunset() { [[ "${!1}" != 'x' ]] && [[ "${!1-x}" == 'x' ]] && echo 1; }
isset()   { [ -z "$(isunset "$1")" ] && echo 1; }

Пример:

$ unset foo; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's unset
$ foo=     ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
$ foo=bar  ; [[ $(isunset foo) ]] && echo "It's unset" || echo "It's set"
It's set
Джонатан Х
источник