Como verifico se existe uma variável em uma instrução 'if'?

69

Eu preciso verificar a existência de uma variável em uma ifdeclaração. Algo para o efeito de:

if [ -v $somevar ]
then
    echo "Variable somevar exists!"
else
    echo "Variable somevar does not exist!"

E a pergunta mais próxima disso foi essa , que na verdade não responde à minha pergunta.

Interessante...
fonte
Se você deseja definir $somevarpara um valor / string se a variável não existe: ${somevar:=42}.
Cyrus
Pessoalmente, costumo verificar apenas o vazio ( [ -n "$var" ]ou [ ! -z "$var" ]). Eu acho que as verificações de existência / inexistência são muito sutis e eu prefiro meu código grosso e simples.
PSKocik 01/12/2015
Por que você precisa desse vs [ -n "$var" ]? Relacionado: stackoverflow.com/questions/3601515/…
Ciro Santilli escreveu:

Respostas:

97

No bash moderno (versão 4.2 e superior):

[[ -v name_of_var ]]

De help test:

-v VAR, True se a variável de shell VAR estiver configurada

Chris Down
fonte
3
Também trabalha com colchetes simples: [ -v name_of_var ].
Meu
7
cuidado para que hashes e matrizes retornem false, a menos que a variável tenha um elemento da chave / índice "0". Para namerefs, ele testa se o destino está definido. Ele não funciona para os parâmetros especiais, como $1, $-, $#...
Stéphane Chazelas
4
Esse recurso é apenas no bash embutido testou [; Não está disponível em /usr/bin/test. Compare man testcom help test.
precisa saber é o seguinte
@MarkLakata Certo, porque comandos externos não podem conhecer o estado interno do shell.
Chris Down
hum não deve ser sempre [[-v "$ name_of_var"]]?
Alexander Mills
24

Depende do que você quer dizer com existe .

Será uma variável que foi declarada, mas não atribuído existir ?

Será uma variável de matriz (ou de hash) que foi atribuída uma lista vazia existir ?

Será uma variável que aponta nameref a uma variável que não está actualmente atribuído existir ?

Você considera $-, $#, $1variáveis? (POSIX não).

Nas conchas tipo Bourne, o caminho canônico é:

if [ -n "${var+set}" ]; then
  echo '$var was set'
fi

Que trabalha para variáveis escalares e outros parâmetros de dizer se uma variável tem sido atribuído um valor (vazio ou não, automaticamente, a partir do ambiente, assigments, read, forou outras).

Para os reservatórios que têm um typesetou declarecomando, que não iria relatar como definir as variáveis que foram declaradas , mas não atribuídos exceto em zsh.

Para shells que suportam matrizes, exceto yashe zshque não reportariam como variáveis ​​de matriz definidas , a menos que o elemento do índice 0 tenha sido definido.

Para bash(mas não ksh93nem zsh), para variáveis ​​do tipo matriz associativa , que não as reportariam como definidas, a menos que seu elemento da chave "0" tenha sido definido.

Para ksh93e bash, para variáveis ​​do tipo nameref , isso só retornará true se a variável referenciada pelo nameref for considerada configurada .

Para ksh, zshe bash, uma abordagem potencialmente melhor poderia ser:

if ((${#var[@]})); then
  echo '$var (or the variable it references for namerefs) or any of its elements for array/hashes has been set'
fi

Para ksh93, zshe bash4.4 ou acima, há também:

if typeset -p var 2> /dev/null | grep -q '^'; then
  echo '$var exists'
fi

Que reportará variáveis ​​que foram definidas ou declaradas.

Stéphane Chazelas
fonte
11
declare -p/ typeset -ptrabalha bashagora também.
cas 2/16
11
@cas, não. Não para variáveis ​​declaradas, mas não configuradas. Experimente bash -c 'typeset -i a; typeset -p a'e compare com ksh93ou zsh.
Stéphane Chazelas
+1 Obrigado por me indicar aqui. Veja também minha pergunta unix.stackexchange.com/q/280893/674
Tim
@cas, que mudaram com o bash-4.4 (lançado em setembro de 2016), Eu editei que no.
Stéphane Chazelas
9

Como mencionado na resposta no SO , aqui está uma maneira de verificar:

if [ -z ${somevar+x} ]; then echo "somevar is unset"; else echo "somevar is set to '$somevar'"; fi

onde $ {somevar + x} é uma expansão de parâmetro que avalia como nulo se var estiver desativado e substitui a string "x" caso contrário.

O uso -n, conforme sugerido pela outra resposta, verificará apenas se a variável contém uma sequência vazia. Não verificará sua existência.

shivams
fonte
2
Você precisa citar $somevarpara lidar IFS=x. Ou isso ou citação x.
mikeserv
11
@ mikeserv Obrigado, eu gosto de aprender sobre casos extremos :) Você quer dizer if [ -z "${somevar+x}" ]? A citação ainda seria necessária dentro [[e ]]?
Tom Hale
@ TomHale - sim, em casos raros. as [ testrotinas aceitam parâmetros da linha de comando e, portanto, deve-se confiar nas expansões e interpretações usuais, conforme ordenadas da maneira usual, para tornar na invocação do teste aplicado o que você deve fazer com que seja lido por qualquer linha de comando do programa. teste {! + "!"}
mikeserv 10/10
@mikeserv Acho que o seu sim é a minha segunda pergunta ... O primeiro também é um sim?
21718 Tom Hale #
11
+1; essa parece ser a maneira mais simples de fazer isso quando set -uestiver em vigor e a versão do Bash for anterior à 4.2.
Kyle Strand
4

POSIXly:

! (: "${somevar?}") 2>/dev/null && echo somevar unset

ou você pode deixar seu shell mostrar a mensagem para você:

(: "${somevar?}")
zsh: somevar: parameter not set
cuonglm
fonte
@ MikeServ: Sim, claro, há muitas maneiras de fazê-lo. Em primeiro lugar, acho que vou duplicar com isso . Mas nesta questão, o OP quer apenas verificar, ele não afirmou que deseja sair ou relatar se a variável não está definida, então eu vim com um subshell de check-in.
cuonglm
2
Bem, eu sei, mas é precisamente porque você tem que fazer isso em um subshell como o que indica que pode não ser a melhor maneira de testar aqui - essa é uma ação de interrupção em caso de falha, não permite nenhum meio simples de lidar uma falha. Portably, mesmo um trapsó pode funcionar em EXIT. É tudo o que estou dizendo - simplesmente não se aplica como um passe / falha muito bem. E também não sou eu que falei - já fiz exatamente isso antes e demorou um pouco para comentar sobre isso para me convencer. Então, eu apenas pensei em pagar adiante.
mikeserv
11
@ MikeServ: Bem, bem, é um bom ponto. Só me pergunto, não acrescentando que pode fazer a confundir OP com a sintaxe :)
cuonglm
2
if set|grep '^somevar=' >/dev/null;then
    echo "somevar exists"
else
    echo "does not exist"
fi
Mel
fonte
Eu imagino que isso seja um pouco ineficiente, mas é muito simples e shcompatível, e é exatamente o que eu preciso.
hoijui 28/08
2
printf ${var+'$var exists!\n'}

... não imprimirá nada quando não estiver. Ou...

printf $"var does%${var+.}s exist%c\n" \ not !

... vou te dizer de qualquer maneira.

você pode usar o valor de retorno de um teste para expandir dinamicamente para a sequência de formato apropriada para sua condição:

[ "${var+1}" ]
printf $"var does%.$?0s exist%c\n" \ not !

Você também pode printffalhar com base em uma substituição ...

printf $"var does%${var+.}s exist%c\n%.${var+b}d" \
        \ not ! \\c >&"$((2${var+-1}))" 2>/dev/null

... que imprime $var does not exist!em stderr e retorna diferente de 0 quando $varestá desativado, mas imprime $var does exist!em stdout e retorna 0 quando $varestá definido.

mikeserv
fonte
1

Esta linha simples funciona (e funciona na maioria dos shells POSIX):

${var+"false"} && echo "var is unset"

Ou, escrito em uma forma mais longa:

unset var

if ${var+"false"}
then
   echo "var is unset"
fi

A expansão é:

  • Se o var tiver um valor (mesmo nulo), false será substituído
  • Se o var não tiver "nenhum valor", "nenhum valor" (nulo) será substituído.

A ${var+"false"}expansão se expande para "nulo" de "falso".
Em seguida, "nothing" ou "false" é executado e o código de saída é definido.

Não há necessidade de chamar o comando test( [ou [[), pois o valor de saída é definido pela (execução) da própria expansão.


fonte
Sim, exceto em algumas versões antigas do zsh na emulação sh quando $IFScontém f, a, l, s ou e. Como em outras respostas, há o caso de matrizes, hashes ou outros tipos de variáveis ​​que podemos mencionar.
Stéphane Chazelas
@ StéphaneChazelas eu escrevi most POSIX shells. most significaIn the greatest number of instances , não todos. ... ... Então, sim, em uma condição obscura when $IFS contains f, a, l, s or ee, para alguma casca obscura, some old versions of zshisso falha: que choque! Eu devo assumir que esse bug foi resolvido há muito tempo. ... ... Você está propondo que devemos escrever código para conchas quebradas há muito tempo ?.
@ StéphaneChazelas Também: O título da pergunta é muito específico: Bash.
zebra binária ???
mikeserv
0

O caminho da casca pura:

[ "${var+1}" ] || echo "The variable has not been set"

Script de teste:

#!/bin/sh
echo "Test 1, var has not yet been created"
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 2, var=1"
var=1
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 3, var="
var=
[ "${var+1}" ] || echo "The variable has not been set"

echo "Test 4, unset var"
unset var
[ "${var+1}" ] || echo "The variable has not been set"
echo "Done"

Resultados:

Test 1, var has not yet been created
The variable has not been set
Test 2, var=1
Test 3, var=
Test 4, unset var
The variable has not been set
Done
Andreas Mikael Bank
fonte
11
Falha quando variável definida como sequência nula.
Tom Hale
0

Com o bash 4.4.19, o seguinte funcionou para mim. Aqui está um exemplo completo

$export MAGENTO_DB_HOST="anyvalue"

#!/bin/bash

if [ -z "$MAGENTO_DB_HOST" ]; then
    echo "Magento variable not set"
else
    echo $MAGENTO_DB_HOST
fi
Aftab Naveed
fonte
-1

Você não pode usar o ifcomando para verificar a existência de variáveis ​​declaradas no bash, no entanto, a -vopção existe no bash mais recente, mas não é portátil e você não pode usá-lo em bashversões mais antigas. Porque quando você estiver usando uma variável, se ela não existir, ela nascerá ao mesmo tempo.

Por exemplo, imagine que eu não usei ou atribui um valor à MYTESTvariável, mas quando você está usando o comando echo, isso não mostra nada! Ou se você estiver usando if [ -z $MYTEST ], retornou valor zero! Não retornou outro status de saída, o que informa que essa variável não existe!

Agora você tem duas soluções (sem -vopção):

  1. Usando o declarecomando
  2. Usando o setcomando

Por exemplo:

MYTEST=2
set | grep MYTEST
declare | grep MYTEST

Infelizmente, esses comandos também mostram funções carregadas na memória! Você pode usar o declare -p | grep -q MYTEST ; echo $?comando para obter resultados mais limpos.

Sepahrad Salour
fonte
-1

Função para verificar se a variável é declarada / não definida

incluindo vazio $array=()


Além da resposta de @ Gilles

case " ${!foobar*} " in
  *" foobar "*) echo "foobar is declared";;
  *) echo "foobar is not declared";;
esac

- que eu não encontrar uma maneira para encapsular-lo dentro de uma função - Eu gostaria de adicionar uma versão simples, que é parcialmente baseado em Richard Hansen 's resposta , mas faz endereço também a armadilha que ocorre com um vazio array=():

# The first parameter needs to be the name of the variable to be checked.
# (See example below)

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}
  • Testando primeiro se a variável está (des) configurada, a chamada para declarar pode ser evitada, se não for necessário.
  • Se, no entanto, $1contiver o nome de um vazio $array=(), a chamada para declarar garantirá o resultado correto
  • Nunca há muitos dados passados ​​para / dev / null, pois declare é chamado apenas se a variável estiver desmarcada ou uma matriz vazia.


Com o seguinte código, as funções podem ser testadas:

( # start a subshell to encapsulate functions/vars for easy copy-paste into the terminal
  # do not use this extra parenthesis () in a script!

var_is_declared() {
    { [[ -n ${!1+anything} ]] || declare -p $1 &>/dev/null;}
}

var_is_unset() {
    { [[ -z ${!1+anything} ]] && ! declare -p $1 &>/dev/null;} 
}

:;       echo -n 'a;       '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=;      echo -n 'a=;      '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a="sd";  echo -n 'a="sd";  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=();    echo -n 'a=();    '; var_is_declared a && echo "# is declared" || echo "# is not declared"
a=("");  echo -n 'a=("");  '; var_is_declared a && echo "# is declared" || echo "# is not declared"
unset a; echo -n 'unset a; '; var_is_declared a && echo "# is declared" || echo "# is not declared"
echo ;
:;       echo -n 'a;       '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=;      echo -n 'a=;      '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a="foo"; echo -n 'a="foo"; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=();    echo -n 'a=();    '; var_is_unset a && echo "# is unset" || echo "# is not unset"
a=("");  echo -n 'a=("");  '; var_is_unset a && echo "# is unset" || echo "# is not unset"
unset a; echo -n 'unset a; '; var_is_unset a && echo "# is unset" || echo "# is not unset"
)

O script deve retornar

a;       # is not declared
a=;      # is declared
a="foo"; # is declared
a=();    # is declared
a=("");  # is declared
unset a; # is not declared

a;       # is unset
a=;      # is not unset
a="foo"; # is not unset
a=();    # is not unset
a=("");  # is not unset
unset a; # is unset
Martin Rüegg
fonte
-1

função bash que funciona para os tipos escalar e matriz :

definição

has_declare() { # check if variable is set at all
    local "$@" # inject 'name' argument in local scope
    &>/dev/null declare -p "$name" # return 0 when var is present
}

invocação

if has_declare name="vars_name" ; then
   echo "variable present: vars_name=$vars_name"
fi
Andrei Pozolotin
fonte