Teste se uma variável é definida em bash ao usar “set -o nounset”

95

O código a seguir sai com um erro de variável não associada. Como consertar isso, ainda usando a set -oopção nounset?

#!/bin/bash

set -o nounset

if [ ! -z ${WHATEVER} ];
 then echo "yo"
fi

echo "whatever"
vinodkone
fonte

Respostas:

98
#!/bin/bash

set -o nounset


VALUE=${WHATEVER:-}

if [ ! -z ${VALUE} ];
 then echo "yo"
fi

echo "whatever"

Nesse caso, VALUEacaba sendo uma string vazia se WHATEVERnão for configurada. Estamos usando a {parameter:-word}expansão, que você pode pesquisar em man bash"Expansão de parâmetros".

Angelom
fonte
14
apenas substitua se [! -z $ {VALUE}]; com if [! -z $ {O QUE QUER QUE: -}];
Angelom de
18
:-verifica se a variável não está definida ou está vazia. Se você quiser verificar a única seja desactivado, usar -: VALUE=${WHATEVER-}. Além disso, uma maneira mais legível de verificar se uma variável está vazia:if [ "${WHATEVER+defined}" = defined ]; then echo defined; else echo undefined; fi
10b0
1
Além disso, isso não funcionará se $WHATEVERcontiver apenas espaços em branco - veja minha resposta.
10b0
9
Existe uma razão para usar " ! -z" em vez de apenas " -n"?
Jonathan Hartley
31

Você precisa citar as variáveis ​​se quiser obter o resultado esperado:

check() {
    if [ -n "${WHATEVER-}" ]
    then
        echo 'not empty'
    elif [ "${WHATEVER+defined}" = defined ]
    then
        echo 'empty but defined'
    else
        echo 'unset'
    fi
}

Teste:

$ unset WHATEVER
$ check
unset
$ WHATEVER=
$ check
empty but defined
$ WHATEVER='   '
$ check
not empty
l0b0
fonte
Eu tentei isso e eu estou surpreso isso funciona ... Tudo está correto, exceto de acordo com "info bash", "${WHATEVER-}"deveria ter um ":"(cólon) antes do "-"(traço) como: "${WHATEVER:-}"e "${WHATEVER+defined}"deve ter uma vírgula antes do "+"(plus) como: "${WHATEVER:+defined}". Para mim, funciona de qualquer maneira, com ou sem o cólon. Em algumas versões do 'nix, provavelmente não funcionará sem incluir os dois pontos, então provavelmente deve ser adicionado.
Kevin Fegan
8
Não, -, +, :+, e :-são todos suportados. O primeiro detecta se a variável está definida e o último detecta se está definida ou vazia . De man bash: "Omitindo os dois pontos resulta em um teste apenas para um parâmetro que não está definido."
10b0
1
Deixa pra lá =). Você está certo. Não sei como perdi isso.
Kevin Fegan
Dos documentos : Dito de outra forma, se os dois pontos forem incluídos, o operador testa a existência de ambos os parâmetros e se seu valor não é nulo; se os dois pontos forem omitidos, o operador testa apenas a existência.
Acumenus,
8

Que tal um oneliner?

[ -z "${VAR:-}" ] && echo "VAR is not set or is empty" || echo "VAR is set to $VAR"

-z verifica se há uma variável vazia ou não definida

NublaII
fonte
2
Não, -zverifica apenas se o próximo parâmetro está vazio. -zé apenas um argumento do [comando. A expansão variável acontece antes que [ -zpossamos fazer qualquer coisa.
Dolmen
1
Esta parece ser a solução correta, pois não gera um erro se $ VAR não estiver definido. @dolmen você pode fornecer um exemplo de quando não funcionaria?
Chris Stryczynski de
@dolmen tendo lido vários recursos do bash sobre expansão de param e achado as outras respostas muito complicadas, não vejo nada de errado com este. então o seu “esclarecimento”, embora tecnicamente correto, parece um tanto inútil na prática, a menos que você precise diferenciar não definido de vazio. Testei não definido, vazio e não vazio (bash 4) e praticamente fez o que era anunciado todas as vezes.
JL Peyret
8

Premissas:

$ echo $SHELL
/bin/bash
$ /bin/bash --version | head -1
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
$ set -o nounset

Se você quiser que um script não interativo imprima um erro e saia se uma variável for nula ou não configurada:

$ [[ "${HOME:?}" ]]

$ [[ "${IAMUNBOUND:?}" ]]
bash: IAMUNBOUND: parameter null or not set

$ IAMNULL=""
$ [[ "${IAMNULL:?}" ]]
bash: IAMNULL: parameter null or not set

Se você não quiser que o script saia:

$ [[ "${HOME:-}" ]] || echo "Parameter null or not set."

$ [[ "${IAMUNBOUND:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

$ IAMNULL=""
$ [[ "${IAMUNNULL:-}" ]] || echo "Parameter null or not set."
Parameter null or not set.

Você pode até usar [e em ]vez de [[e ]]acima, mas o último é preferível no Bash.

Observe o que os dois pontos fazem acima. Dos documentos :

Em outras palavras, se os dois pontos forem incluídos, o operador testa a existência de ambos os parâmetros e se seu valor não é nulo; se os dois pontos forem omitidos, o operador testa apenas a existência.

Aparentemente, não há necessidade de -nou -z.

Em resumo, normalmente posso apenas usar [[ "${VAR:?}" ]]. Pelos exemplos, isso imprime um erro e sai se uma variável for nula ou não configurada.

Acumenus
fonte
4

Você pode usar

if [[ ${WHATEVER:+$WHATEVER} ]]; then

mas

if [[ "${WHATEVER:+isset}" == "isset" ]]; then

pode ser mais legível.

Aleš Friedl
fonte
As comparações de strings devem usar o =operador padrão (POSIX) , não ==para auxiliar na portabilidade e, se possível , em [vez disso [[.
Jens,
2
@Jens A questão é específica do bash e inclui o set -o nounsetque é específico do bash. Se você colocar um #!/bin/bashno topo do seu script, é realmente melhor usar as melhorias do bash.
Bruno Bronosky
0

Embora este não seja exatamente o caso de uso solicitado acima, descobri que, se você deseja usarnounset (ou -u) o comportamento padrão é o que você deseja: sair do valor diferente de zero com uma mensagem descritiva.

Levei tanto tempo para perceber isso que achei que valia a pena postar como uma solução.

Se tudo o que você quiser é ecoar outra coisa ao sair, ou fazer alguma limpeza, você pode usar uma armadilha.

O :-operador provavelmente é o que você deseja de outra forma.

Max Bileschi
fonte