Usando unset vs. definindo uma variável para vazia

108

No momento, estou escrevendo uma estrutura de teste bash, onde em uma função de teste, tanto os testes padrão do bash ( [[) quanto os matchers predefinidos podem ser usados. Matchers são wrappers para '[[' e além de retornar um código de retorno, defina alguma mensagem significativa dizendo o que era esperado.

Exemplo:

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

            return 1
    fi
}

Portanto, quando um matcher é usado e falha, somente então um error_message é definido.

Agora, em algum ponto depois, eu testo se os testes foram bem-sucedidos. Se deu certo, imprimo a expectativa em verde, se falhou em vermelho.

Além disso, pode haver um error_message definido, então eu testo se uma mensagem existe, imprimo-a e, em seguida, cancelo a definição (porque o teste a seguir pode não definir um error_message):

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

    unset -v error_message
fi

Agora minha pergunta é, se é melhor remover a variável ou apenas defini-la como '', como

error_message=''

Qual é o melhor? Isso realmente faz diferença? Ou talvez eu deva ter um sinalizador adicional indicando que a mensagem foi definida?

método auxiliar
fonte
1
Se você nunca comparar error_messagecom qualquer outra coisa, eu diria que não importa. No entanto, acho que você deseja [[ $error_message ]], caso contrário, você está testando se a string literal "mensagem_erro" existe.
chepner
@chepner Sim, foi um erro de digitação. Corrigido.
método do helper

Respostas:

143

Geralmente você não vê diferença, a menos que esteja usando 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

Então, realmente, depende de como você vai testar a variável.

Acrescentarei que a minha forma preferida de testar, se estiver definida, é:

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

ou

[[ -z $var ]]  # True if zero length
cdarke
fonte
43
var=não está "não definido". É apenas uma string vazia não citada. Além disso set -u, as várias formas de expansão de parâmetro do bash também podem distinguir entre valores não definidos e nulos: ${foo:bar}expande para "bar" quando foonão está definido, mas "" quando fooé nulo, enquanto se ${foo:-bar}expande para "bar" se foo não está definido ou é nulo.
chepner
1
[[ -n $var ]]é falso se varfor definido como uma string vazia.
chepner
1
se você usar 'declare -p' para verificar a variável 'existência' então var = mostrará 'declare - var = ""' você deve usar unset para se livrar dela, é claro que se for uma variável somente leitura, você não pode obter livrar-se disso, é claro. Além disso, se você var = algo dentro de uma função, não precisará se preocupar em se livrar dela se usar "local var = value", mas tome cuidado, porque "declare var = value" também é local. No caso de declare você deve definir explicitamente como global usando "declare -g var = value", sem declarar você deve definir explicitamente como local usando "local". Vars globais são apenas apagados / w não definidos.
osirisgothra
@osirisgothra: bom ponto sobre variáveis ​​locais. Obviamente, geralmente é melhor declarar (ou localizar) todas as variáveis ​​em uma função para que seja encapsulada.
cdarke
3
@chepner deve ser em ${foo-bar}vez de ${foo:bar}. teste por si mesmo:unset a; echo ">${a:-foo}-${a:foo}-${a-foo}<"
Liviu Chircu
17

Como já foi dito, usar unset é diferente com matrizes também

$ foo=(4 5 6)

$ foo[2]=

$ echo ${#foo[*]}
3

$ unset foo[2]

$ echo ${#foo[*]}
2
Steven Penny
fonte
2

Portanto, ao remover a definição do índice 2 do array, você essencialmente remove aquele elemento do array e diminui o tamanho do array (?).

Eu fiz meu próprio teste ..

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

O que resulta em..

3
0

Portanto, apenas para esclarecer que remover a configuração de todo o array irá removê-lo completamente.

PdC
fonte
0

Com base nos comentários acima, aqui está um teste simples:

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

Exemplo:

$ 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
Jonathan H
fonte