Como verificar se uma variável está definida no Bash?

1568

Como sei se uma variável está definida no Bash?

Por exemplo, como verifico se o usuário deu o primeiro parâmetro para uma função?

function a {
    # if $1 is set ?
}
prosseek
fonte
12
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi.
Jens
210
Nota para quem procura uma solução: Existem muitas respostas altamente classificadas para esta pergunta que respondem à pergunta "é variável não vazia". As mais soluções de correção ("conjunto de variáveis") são mencionadas nas respostas de Jens e Lionel abaixo.
Nathan Kidd
8
Russell Harmon e Seamus também estão corretos em seus -vtestes, embora isso pareça estar disponível apenas em novas versões bashe não portável em conchas.
Graeme
5
Conforme apontado por @NathanKidd, as soluções corretas são fornecidas por Lionel e Jens. prosseek, você deve mudar sua resposta aceita para uma delas.
Garrett
3
... ou a resposta incorreta pode ter o voto negativo dos mais exigentes entre nós, já que @prosseek não está resolvendo o problema.
Jul3

Respostas:

2305

(Normalmente) O caminho certo

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

onde ${var+x}é uma expansão de parâmetro que avalia como nada se não varestiver definida e substitui a string de xoutra forma.

Cotação Digressão

As aspas podem ser omitidas (é o que podemos dizer em ${var+x}vez de "${var+x}"), porque essa sintaxe e uso garantem que isso só será expandido para algo que não requer aspas (uma vez que se expande para x(que não contém quebras de palavras e não precisa de aspas) ou para nada (que resulta em [ -z ], que avalia convenientemente o mesmo valor (verdadeiro) que [ -z "" ]também)).

No entanto, embora as aspas possam ser omitidas com segurança, e isso não foi imediatamente óbvio para todos (nem sequer ficou claro para o primeiro autor desta explicação de citações que também é um dos principais codificadores do Bash), às vezes seria melhor escrever a solução entre aspas [ -z "${var+x}" ], pelo menor custo possível de uma penalidade de velocidade O (1). O primeiro autor também adicionou isso como um comentário ao lado do código usando esta solução, fornecendo o URL para esta resposta, que agora também inclui a explicação de por que as aspas podem ser omitidas com segurança.

(Muitas vezes) O caminho errado

if [ -z "$var" ]; then echo "var is blank"; else echo "var is set to '$var'"; fi

Isso geralmente está errado porque não faz distinção entre uma variável que está desconfigurada e uma variável que está configurada para a cadeia vazia. Ou seja, se var='', então, a solução acima produzirá "var is blank".

A distinção entre não definido e "definido para a cadeia vazia" é essencial em situações em que o usuário precisa especificar uma extensão ou lista adicional de propriedades, e que não a especificação dos padrões é um valor não vazio, enquanto a cadeia vazia deve ser especificada. faça com que o script use uma extensão vazia ou uma lista de propriedades adicionais.

A distinção pode não ser essencial em todos os cenários. Nesses casos [ -z "$var" ], tudo ficará bem.

Lionel
fonte
29
@Garrett, sua edição fez esta resposta incorreta, ${var+x}é a substituição correta a ser usada. Usar [ -z ${var:+x} ]não produz resultados diferentes de [ -z "$var" ].
Graeme
11
Isso não funciona. Estou recebendo "não definido", independentemente de var ser definido como um valor ou não (limpo com "unset var", "echo $ var" não produz saída).
Brent212 9/09/14
10
Para a sintaxe da solução $ {parameter + word}, ​​a seção do manual oficial é gnu.org/software/bash/manual/… ; no entanto, um erro nisso, ele não menciona muito claramente essa sintaxe, mas diz apenas citar (omitir os dois pontos [":"] resulta em um teste apenas para um parâmetro não definido. .. $ {parameter: + word} [significa] Se o parâmetro for nulo ou não definido, nada será substituído; caso contrário, a expansão da palavra será substituída.); a referência citada pubs.opengroup.org/onlinepubs/9699919799/utilities/… (é bom saber) tem documentos muito mais claros aqui.
Destino Architect
7
Parece que aspas são necessárias quando você usa várias expressões, por exemplo, [ -z "" -a -z ${var+x} ]entra bash: [: argument expectedno bash 4.3-ubuntu (mas não, por exemplo, zsh). Eu realmente não gosto da resposta usando uma sintaxe que funciona em alguns casos.
FauxFaux
41
Usar um simples [ -z $var ]não é mais "errado" do que omitir aspas. De qualquer forma, você está fazendo suposições sobre sua entrada. Se você está bem tratando uma string vazia como não definida, [ -z $var ]é tudo o que precisa.
Nshew 5/05
910

Para verificar a variável de sequência não nula / diferente de zero, ou seja, se configurada, use

if [ -n "$1" ]

É o oposto de -z. Eu me pego usando -nmais de -z.

Você usaria como:

if [ -n "$1" ]; then
  echo "You supplied the first parameter!"
else
  echo "First parameter not supplied."
fi
mbrannig
fonte
86
Normalmente, prefiro o [[ ]]excesso [ ], pois [[ ]]é mais poderoso e causa menos problemas em determinadas situações (veja esta pergunta para obter uma explicação da diferença entre os dois). A pergunta pede especificamente uma solução bash e não menciona nenhum requisito de portabilidade.
Fluxo
18
A questão é como se pode ver se uma variável está definida . Então você não pode assumir que a variável está definida.
hellogoodbye
7
Concordo, []funciona melhor especialmente para scripts shell (não bash).
Hengjie
73
Falha set -u.
Ciro Santilli escreveu
63
Observe que esse -né o teste padrão, de forma mais simples [ "$1" ]ou também [[ $1 ]]funciona. Observe também que a [[ ]] citação é desnecessária .
tne
478

Veja como testar se um parâmetro está desmarcado ou vazio ("Nulo") ou definido com um valor :

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |       parameter      |     parameter   |    parameter    |
|   in script:       |   Set and Not Null   |   Set But Null  |      Unset      |
+--------------------+----------------------+-----------------+-----------------+
| ${parameter:-word} | substitute parameter | substitute word | substitute word |
| ${parameter-word}  | substitute parameter | substitute null | substitute word |
| ${parameter:=word} | substitute parameter | assign word     | assign word     |
| ${parameter=word}  | substitute parameter | substitute null | assign word     |
| ${parameter:?word} | substitute parameter | error, exit     | error, exit     |
| ${parameter?word}  | substitute parameter | substitute null | error, exit     |
| ${parameter:+word} | substitute word      | substitute null | substitute null |
| ${parameter+word}  | substitute word      | substitute word | substitute null |
+--------------------+----------------------+-----------------+-----------------+

Fonte: POSIX: Expansão do parâmetro :

Em todos os casos mostrados com "substituto", a expressão é substituída pelo valor mostrado. Em todos os casos mostrados com "assign", o parâmetro recebe esse valor, o que também substitui a expressão.

Para mostrar isso em ação:

+--------------------+----------------------+-----------------+-----------------+
|   Expression       |  FOO="world"         |     FOO=""      |    unset FOO    |
|   in script:       |  (Set and Not Null)  |  (Set But Null) |     (Unset)     |
+--------------------+----------------------+-----------------+-----------------+
| ${FOO:-hello}      | world                | hello           | hello           |
| ${FOO-hello}       | world                | ""              | hello           |
| ${FOO:=hello}      | world                | FOO=hello       | FOO=hello       |
| ${FOO=hello}       | world                | ""              | FOO=hello       |
| ${FOO:?hello}      | world                | error, exit     | error, exit     |
| ${FOO?hello}       | world                | ""              | error, exit     |
| ${FOO:+hello}      | hello                | ""              | ""              |
| ${FOO+hello}       | hello                | hello           | ""              |
+--------------------+----------------------+-----------------+-----------------+
Jens
fonte
5
@ HelloGoodbye Sim, funciona: set foo; echo ${1:-set but null or unset}ecos "foo"; set --; echo ${1:-set but null or unset}ecos set, mas nulo ...
Jens
4
@HelloGoodbye Os parâmetros de posição pode ser definida com, uh, set:-)
Jens
95
Esta resposta é muito confusa. Algum exemplo prático de como usar esta tabela?
Ben Davis
12
@BenDavis Os detalhes são explicados no link ao padrão POSIX, que faz referência ao capítulo em que a tabela é retirada. Uma construção que eu uso em quase todos os scripts que escrevo é : ${FOOBAR:=/etc/foobar.conf}definir um valor padrão para uma variável se ela estiver desmarcada ou nula.
Jens
1
parameteré qualquer nome de variável. wordé uma cadeia de caracteres a ser substituída, dependendo da sintaxe da tabela que você está usando e se a variável $parameterestá definida, definida, mas nula ou não definida. Não para tornar as coisas mais complicadas, mas wordtambém pode incluir variáveis! Isso pode ser muito útil para passar argumentos opcionais separados por um caractere. Por exemplo: ${parameter:+,${parameter}}gera uma vírgula separada ,$parameterse estiver definida, mas não nula. Neste caso, wordé,${parameter}
TrinitronX 19/01
260

Embora a maioria das técnicas descritas aqui esteja correta, o bash 4.2 suporta um teste real para a presença de uma variável ( man bash ), em vez de testar o valor da variável.

[[ -v foo ]]; echo $?
# 1

foo=bar
[[ -v foo ]]; echo $?
# 0

foo=""
[[ -v foo ]]; echo $?
# 0

Notavelmente, essa abordagem não causará um erro quando usada para verificar uma variável não definida no modo set -u/ set -o nounset, diferente de muitas outras abordagens, como o uso [ -z.

Russell Harmon
fonte
9
No bash 4.1.2, independentemente de a variável estar definida, [[ -v aaa ]]; echo $?==>-bash: conditional binary operator expected -bash: syntax error near 'aaa'
Dan
32
O argumento '-v' para o 'teste' interno foi adicionado no bash 4.2.
Ti Strga
19
o argumento -v foi adicionado como um complemento à opção -u shell (nounset). Com o 'nounset' ativado (set -u), o shell retornará um erro e será encerrado, se não for interativo. $ # foi bom para verificar os parâmetros posicionais. No entanto, as variáveis ​​nomeadas precisavam de outra maneira, além de atrapalhar, para identificá-las como não definidas. É lamentável que esse recurso tenha chegado tão tarde, porque muitos tendem a ser compatíveis com a versão anterior; nesse caso, eles não podem usá-lo. A única outra opção é usar truques ou 'hacks' para contorná-lo, como mostrado acima, mas é mais deselegante.
Osirisgothra
2
Observe que isso não funciona para variáveis ​​declaradas, mas não definidas. por exemplo,declare -a foo
miken32
17
É por isso que continuo lendo depois da resposta aceita / com a melhor votação.
24418 Joe
176

Existem várias maneiras de fazer isso, sendo uma delas a seguinte:

if [ -z "$1" ]

Isso será bem-sucedido se $ 1 for nulo ou não definido

ennuikiller
fonte
46
Há uma diferença entre um parâmetro não definido e um parâmetro com um valor nulo.
chepner
21
Eu só quero ser pedante e ressaltar que esta é a resposta oposta ao que a pergunta coloca. As perguntas perguntam se a variável está definida, não se a variável não está definida.
Philluminati 3/13
47
[[ ]]nada mais é que uma armadilha da portabilidade. if [ -n "$1" ]deve ser usado aqui.
Jens
73
Esta resposta está incorreta. A pergunta pede uma maneira de saber se uma variável está definida, não se está definida para um valor não vazio. Dado foo="", $footem um valor, mas -zinforma apenas que está vazio. E -z $foovai explodir se você tiver set -o nounset.
Keith Thompson
4
Depende da definição de "variável está configurada". Se você deseja verificar se a variável está definida como uma sequência diferente de zero , a resposta mbranning está correta. Mas se você quiser verificar se a variável é declarada, mas não inicializada (ie foo=), a resposta de Russel está correta.
Fluxo
77

Eu sempre acho a tabela POSIX na outra resposta lenta para grok, então aqui está a minha opinião:

   +----------------------+------------+-----------------------+-----------------------+
   |   if VARIABLE is:    |    set     |         empty         |        unset          |
   +----------------------+------------+-----------------------+-----------------------+
 - |  ${VARIABLE-default} | $VARIABLE  |          ""           |       "default"       |
 = |  ${VARIABLE=default} | $VARIABLE  |          ""           | $(VARIABLE="default") |
 ? |  ${VARIABLE?default} | $VARIABLE  |          ""           |       exit 127        |
 + |  ${VARIABLE+default} | "default"  |       "default"       |          ""           |
   +----------------------+------------+-----------------------+-----------------------+
:- | ${VARIABLE:-default} | $VARIABLE  |       "default"       |       "default"       |
:= | ${VARIABLE:=default} | $VARIABLE  | $(VARIABLE="default") | $(VARIABLE="default") |
:? | ${VARIABLE:?default} | $VARIABLE  |       exit 127        |       exit 127        |
:+ | ${VARIABLE:+default} | "default"  |          ""           |          ""           |
   +----------------------+------------+-----------------------+-----------------------+

Observe que cada grupo (com e sem dois pontos anteriores) tem os mesmos casos definidos e não definidos ; portanto, a única coisa que difere é como os casos vazios são tratados.

Com os dois pontos anteriores, os casos vazios e não configurados são idênticos, então eu usaria aqueles sempre que possível (ou seja :=, use , não apenas =, porque o caso vazio é inconsistente).

Cabeçalhos:

  • definir meios VARIABLEnão está vazio ( VARIABLE="something")
  • significa vazioVARIABLE é vazio / nulo ( VARIABLE="")
  • meios VARIABLEnão definidos não existem ( unset VARIABLE)

Valores:

  • $VARIABLE significa que o resultado é o valor original da variável.
  • "default" significa que o resultado foi a sequência de substituição fornecida.
  • "" significa que o resultado é nulo (uma sequência vazia).
  • exit 127 significa que o script para de executar com o código de saída 127.
  • $(VARIABLE="default")significa que o resultado é o valor original da variável e a sequência de substituição fornecida é atribuída à variável para uso futuro.
deizel
fonte
1
Você corrigiu apenas aqueles que são erros de codificação reais (instâncias de indireção indesejada ao trabalhar com variáveis). Fiz uma edição para corrigir mais alguns casos em que o dólar faz diferença na interpretação da descrição. BTW, todas as variáveis ​​de shell (com exceção de matrizes) são variáveis ​​de string; pode acontecer que eles contenham uma sequência que possa ser interpretada como um número. Falar sobre cordas apenas obscurece a mensagem. Citações não têm nada a ver com seqüências de caracteres, são apenas uma alternativa para escapar. VARIABLE=""poderia ser escrito como VARIABLE=. Ainda assim, o primeiro é mais legível.
Palec
Obrigado pela tabela, é muito útil, no entanto, estou tentando fazer com que o script saia se não estiver definido ou vazio. Mas o código de saída que recebo é 1 e não 127.
Astronauta
64

Para ver se uma variável não é vazia, eu uso

if [[ $var ]]; then ...       # `$var' expands to a nonempty string

O oposto testa se uma variável está desmarcada ou vazia:

if [[ ! $var ]]; then ...     # `$var' expands to the empty string (set or not)

Para ver se uma variável está definida (vazia ou não vazia), eu uso

if [[ ${var+x} ]]; then ...   # `var' exists (empty or nonempty)
if [[ ${1+x} ]]; then ...     # Parameter 1 exists (empty or nonempty)

O oposto testa se uma variável está desabilitada:

if [[ ! ${var+x} ]]; then ... # `var' is not set at all
if [[ ! ${1+x} ]]; then ...   # We were called with no arguments
phkoester
fonte
Eu também tenho usado isso por um tempo; apenas de uma maneira reduzida:[ $isMac ] && param="--enable-shared"
@ Bhaskar Acho que é porque essa resposta já forneceu a mesma resposta (use ${variable+x}para obter o xiff $variabledefinido) quase meio ano antes. Também tem muito mais detalhes explicando por que está certo.
Mark Haferkamp
mas ${variable+x}é menos legível (e óbvia)
knocte
35

Em uma versão moderna do Bash (4.2 ou posterior, acho; não tenho certeza), tentaria o seguinte:

if [ ! -v SOMEVARIABLE ] #note the lack of a $ sigil
then
    echo "Variable is unset"
elif [ -z "$SOMEVARIABLE" ]
then
    echo "Variable is set to an empty string"
else
    echo "Variable is set to some string"
fi
Seamus Connor
fonte
5
Observe também que [ -v "$VARNAME" ]não está incorreto, mas simplesmente executa um teste diferente. Suponha VARNAME=foo; então ele verifica se existe uma variável nomeada fooque está definida.
chepner
5
Nota para aqueles portabilidade querendo, -vnão é compatível com POSIX
kzh
Se alguém recebe [: -v: unexpected operator, é preciso garantir o uso em bashvez de sh.
koppor
25
if [ "$1" != "" ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi

Embora para argumentos seja normalmente melhor testar $ #, que é o número de argumentos, na minha opinião.

if [ $# -gt 0 ]; then
  echo \$1 is set
else
  echo \$1 is not set
fi
Paul Creasey
fonte
1
O primeiro teste é para trás; Eu acho que você quer [ "$1" != "" ](ou [ -n "$1" ]) ...
Gordon Davisson
10
Isso falhará se $1estiver definido como a sequência vazia.
hellogoodbye
2
A segunda parte da resposta (contando parâmetros) é uma solução útil para a primeira parte da resposta que não está funcionando quando $1está definida como uma sequência vazia.
Mark Berry
Isso falhará se set -o nounsetestiver ativado.
Flimm
21

Nota

Estou dando uma resposta fortemente focada no Bash por causa da bashtag.

Resposta curta

Desde que você lide apenas com variáveis ​​nomeadas no Bash, essa função sempre deve informar se a variável foi definida, mesmo que seja uma matriz vazia.

variable-is-set() {
    declare -p "$1" &>/dev/null
}

Por que isso funciona

No Bash (pelo menos em 3.0), se varfor uma variável declarada / definida, declare -p varemite um declarecomando que definiria vara variável para qualquer que seja seu tipo e valor atuais e retorna o código de status 0(êxito). Se varnão for declarado, declare -p varemite uma mensagem de erro stderre retorna o código de status 1. Usando &>/dev/null, redireciona a saída normal stdoute a stderrsaída para /dev/null, para nunca mais ser vista, e sem alterar o código de status. Portanto, a função retorna apenas o código de status.

Por que outros métodos (às vezes) falham no Bash

  • [ -n "$var" ]: Isso verifica apenas se ${var[0]}não está vazio. (No Bash, $varé o mesmo que ${var[0]}.)
  • [ -n "${var+x}" ]: Apenas verifica se ${var[0]}está definido.
  • [ "${#var[@]}" != 0 ]: Isso verifica apenas se pelo menos um índice de $varestá definido.

Quando este método falha no Bash

Isso só funciona para variáveis nomeadas (incluindo $_), e não determinadas variáveis especiais ( $!, $@, $#, $$, $*, $?, $-, $0, $1, $2, ..., e qualquer outro que eu possa ter esquecido). Como nenhuma dessas são matrizes, o estilo POSIX [ -n "${var+x}" ]funciona para todas essas variáveis ​​especiais. Mas tome cuidado para envolvê-lo em uma função, pois muitas variáveis ​​especiais alteram valores / existência quando funções são chamadas.

Nota de compatibilidade do shell

Se o seu script tiver matrizes e você estiver tentando torná-lo compatível com o maior número possível de shells, considere usar em typeset -pvez de declare -p. Eu li que o ksh suporta apenas o primeiro, mas não pude testar isso. Eu sei que o Bash 3.0+ e o Zsh 5.5.1 suportam ambos typeset -pe declare -p, diferindo apenas em qual é uma alternativa para o outro. Mas não testei diferenças além dessas duas palavras-chave e não testei outras conchas.

Se você precisa que seu script seja compatível com POSIX sh, não poderá usar matrizes. Sem matrizes, [ -n "{$var+x}" ]funciona.

Código de comparação para diferentes métodos no Bash

Essa função desativa a variável var, evalé o código passado, executa testes para determinar se varé definido pelo evalcódigo d e, finalmente, mostra os códigos de status resultantes para os diferentes testes.

Estou pulando test -v var, [ -v var ]e [[ -v var ]]porque eles produzem resultados idênticos ao padrão POSIX [ -n "${var+x}" ], exigindo o Bash 4.2+. Também estou pulando typeset -pporque é o mesmo que declare -pnas conchas que testei (Bash 3.0 a 5.0 e Zsh 5.5.1).

is-var-set-after() {
    # Set var by passed expression.
    unset var
    eval "$1"

    # Run the tests, in increasing order of accuracy.
    [ -n "$var" ] # (index 0 of) var is nonempty
    nonempty=$?
    [ -n "${var+x}" ] # (index 0 of) var is set, maybe empty
    plus=$?
    [ "${#var[@]}" != 0 ] # var has at least one index set, maybe empty
    count=$?
    declare -p var &>/dev/null # var has been declared (any type)
    declared=$?

    # Show test results.
    printf '%30s: %2s %2s %2s %2s\n' "$1" $nonempty $plus $count $declared
}

Código do caso de teste

Observe que os resultados do teste podem ser inesperados devido ao Bash tratar os índices não numéricos da matriz como "0" se a variável não tiver sido declarada como uma matriz associativa. Além disso, matrizes associativas são válidas apenas no Bash 4.0+.

# Header.
printf '%30s: %2s %2s %2s %2s\n' "test" '-n' '+x' '#@' '-p'
# First 5 tests: Equivalent to setting 'var=foo' because index 0 of an
# indexed array is also the nonindexed value, and non-numerical
# indices in an array not declared as associative are the same as
# index 0.
is-var-set-after "var=foo"                        #  0  0  0  0
is-var-set-after "var=(foo)"                      #  0  0  0  0
is-var-set-after "var=([0]=foo)"                  #  0  0  0  0
is-var-set-after "var=([x]=foo)"                  #  0  0  0  0
is-var-set-after "var=([y]=bar [x]=foo)"          #  0  0  0  0
# '[ -n "$var" ]' fails when var is empty.
is-var-set-after "var=''"                         #  1  0  0  0
is-var-set-after "var=([0]='')"                   #  1  0  0  0
# Indices other than 0 are not detected by '[ -n "$var" ]' or by
# '[ -n "${var+x}" ]'.
is-var-set-after "var=([1]='')"                   #  1  1  0  0
is-var-set-after "var=([1]=foo)"                  #  1  1  0  0
is-var-set-after "declare -A var; var=([x]=foo)"  #  1  1  0  0
# Empty arrays are only detected by 'declare -p'.
is-var-set-after "var=()"                         #  1  1  1  0
is-var-set-after "declare -a var"                 #  1  1  1  0
is-var-set-after "declare -A var"                 #  1  1  1  0
# If 'var' is unset, then it even fails the 'declare -p var' test.
is-var-set-after "unset var"                      #  1  1  1  1

Saída de teste

As mnemónicas teste na linha de cabeçalho correspondem a [ -n "$var" ], [ -n "${var+x}" ], [ "${#var[@]}" != 0 ], e declare -p var, respectivamente.

                         test: -n +x #@ -p
                      var=foo:  0  0  0  0
                    var=(foo):  0  0  0  0
                var=([0]=foo):  0  0  0  0
                var=([x]=foo):  0  0  0  0
        var=([y]=bar [x]=foo):  0  0  0  0
                       var='':  1  0  0  0
                 var=([0]=''):  1  0  0  0
                 var=([1]=''):  1  1  0  0
                var=([1]=foo):  1  1  0  0
declare -A var; var=([x]=foo):  1  1  0  0
                       var=():  1  1  1  0
               declare -a var:  1  1  1  0
               declare -A var:  1  1  1  0
                    unset var:  1  1  1  1

Sumário

  • declare -p var &>/dev/null é (100%?) confiável para testar variáveis ​​nomeadas no Bash desde pelo menos 3,0.
  • [ -n "${var+x}" ] é confiável em situações compatíveis com POSIX, mas não pode manipular matrizes.
  • Existem outros testes para verificar se uma variável não é vazia e para verificar variáveis ​​declaradas em outros shells. Mas esses testes não são adequados para scripts Bash nem POSIX.
Mark Haferkamp
fonte
3
Das respostas que tentei, essa ( declare -p var &>/dev/null) é a ÚNICA que funciona. OBRIGADO!
user1387866
2
@ mark-haferkamp Tentei editar sua resposta para adicionar o "/" crítico anterior ao seu código de função para que ele lesse, ... &> /dev/nullmas o SO não me permitiria editar um único caractere 🙄 (deve ter> 6 caracteres - até tentei adicionar espaço em branco mas não funcionou). Além disso, pode valer a pena alterar a função para alternar $1para uma amostra denominada variável (por exemplo OUTPUT_DIR), pois, como você apontou, variáveis ​​especiais como $1não funcionam aqui. Enfim, é uma edição crítica, caso contrário, o código está procurando criar um arquivo chamado nullem um diretório relativo chamado dev. Entre - ótima resposta!
Joehanna 23/11/19
Além disso, usando o nome da variável sem o prefixo $ também funciona: declare -p PATH &> /dev/nullretorna 0 onde, como declare -p ASDFASDGFASKDF &> /dev/nullretorna 1. (sobre o bash 5.0.11 (1) -release)
joehanna
1
Ótimo trabalho! 1 palavra de cautela: declare vardeclara var uma variável, mas não a define em termos de set -o nounset: Teste comdeclare foo bar=; set -o nounset; echo $bar; echo $foo
xebeche 25/11/19
@joehanna Obrigado por capturar essa falta /! Corrigi isso, mas acho que a lógica é confusa o suficiente para justificar uma função, especialmente à luz do comentário de @ xebeche. @xebeche Isso é inconveniente para a minha resposta ... Parece que vou ter que tentar algo como declare -p "$1" | grep =ou test -v "$1".
Mark Haferkamp 02/12/19
19

Para aqueles que procuram verificar se não estão definidos ou vazios quando em um script com set -u:

if [ -z "${var-}" ]; then
   echo "Must provide var environment variable. Exiting...."
   exit 1
fi

A [ -z "$var" ]verificação regular falhará com var; unbound variableif, set -umas se [ -z "${var-}" ] expandirá para a string vazia, se varestiver desativada sem falha.

RubenLaguna
fonte
Está faltando dois pontos. Deveria ser ${var:-}, não ${var-}. Mas no Bash, posso confirmar que funciona mesmo sem os dois pontos.
Palec
3
${var:-}e ${var-}significa coisas diferentes. ${var:-}expandirá para a string vazia se varnão estiver definido ou nulo e ${var-}expandirá para a string vazia apenas se não estiver definido.
RubenLaguna
Acabei de aprender algo novo, obrigado! A omissão dos dois pontos resulta em um teste apenas para um parâmetro não definido. Em outras palavras, se os dois pontos estiverem incluídos, o operador testa a existência dos dois parâmetros e se seu valor não é nulo; se os dois pontos forem omitidos, o operador testa apenas a existência. Não faz diferença aqui, mas é bom saber.
Palec
17

Você quer sair se não estiver definido

Isso funcionou para mim. Eu queria que meu script saísse com uma mensagem de erro se um parâmetro não estivesse definido.

#!/usr/bin/env bash

set -o errexit

# Get the value and empty validation check all in one
VER="${1:?You must pass a version of the format 0.0.0 as the only argument}"

Isso retorna com um erro quando é executado

peek@peek:~$ ./setver.sh
./setver.sh: line 13: 1: You must pass a version of the format 0.0.0 as the only argument

Marque apenas, sem saída - Vazio e Desativado são INVÁLIDOS

Tente esta opção se você quiser apenas verificar se o valor configurado = VALID ou não configurado / vazio = INVALID.

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID
if [ "${TUNSET:-}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Ou, mesmo testes breves ;-)

[ "${TSET:-}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY:-}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET:-}" ] && echo "VALID" || echo "INVALID"

Marque apenas, sem saída - Somente vazio é INVÁLIDO

E esta é a resposta para a pergunta. Use isso se você quiser apenas verificar se o valor definido / vazio = VÁLIDO ou não definido = INVÁLIDO.

OBSERVAÇÃO, o "1" em "..- 1}" é insignificante, pode ser qualquer coisa (como x)

TSET="good val"
TEMPTY=""
unset TUNSET

if [ "${TSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TEMPTY+1}" ]; then echo "VALID"; else echo "INVALID";fi
# VALID
if [ "${TUNSET+1}" ]; then echo "VALID"; else echo "INVALID";fi
# INVALID

Testes curtos

[ "${TSET+1}"   ] && echo "VALID" || echo "INVALID"
[ "${TEMPTY+1}" ] && echo "VALID" || echo "INVALID"
[ "${TUNSET+1}" ] && echo "VALID" || echo "INVALID"

Dedico esta resposta a @ mklement0 (comentários) que me desafiou a responder à pergunta com precisão.

Referência http://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_06_02

Jarrod Chesney
fonte
15

Para verificar se uma variável está configurada com um valor não vazio, use [ -n "$x" ], como outros já indicaram.

Na maioria das vezes, é uma boa idéia tratar uma variável que tenha um valor vazio da mesma maneira que uma variável que não esteja definida. Mas você pode distinguir os dois se precisar: [ -n "${x+set}" ]( "${x+set}"expande para setse xestiver definido e para a sequência vazia, se xnão estiver definido ).

Para verificar se um parâmetro foi aprovado, teste $#, que é o número de parâmetros passados ​​para a função (ou para o script, quando não estiver em uma função) (consulte a resposta de Paulo ).

Gilles 'SO- parar de ser mau'
fonte
14

Leia a seção "Expansão de parâmetros" da bashpágina de manual. A expansão de parâmetro não fornece um teste geral para uma variável que está sendo definida, mas há várias coisas que você pode fazer em um parâmetro, se não estiver definido.

Por exemplo:

function a {
    first_arg=${1-foo}
    # rest of the function
}

será first_argigual a $1se for atribuído, caso contrário, ele usará o valor "foo". Se aabsolutamente for necessário usar um único parâmetro e não existir um bom padrão, você poderá sair com uma mensagem de erro quando nenhum parâmetro for fornecido:

function a {
    : ${1?a must take a single argument}
    # rest of the function
}

(Observe o uso de :como um comando nulo, que apenas expande os valores de seus argumentos. Não queremos fazer nada $1neste exemplo, basta sair se não estiver definido)

chepner
fonte
1
Estou confuso que nenhuma das outras respostas mencione o simples e o elegante : ${var?message}.
tripleee
De onde você tirou isso? Maravilhoso! É justo funciona! E é curto e elegante! Obrigado!
Roger Roger
13

No bash, você pode usar -vdentro do [[ ]]built-in:

#! /bin/bash -u

if [[ ! -v SOMEVAR ]]; then
    SOMEVAR='hello'
fi

echo $SOMEVAR
AlejandroVD
fonte
8

Usar [[ -z "$var" ]]é a maneira mais fácil de saber se uma variável foi configurada ou não, mas essa opção -znão faz distinção entre uma variável não configurada e uma variável definida como uma sequência vazia:

$ set=''
$ [[ -z "$set" ]] && echo "Set" || echo "Unset" 
Unset
$ [[ -z "$unset" ]] && echo "Set" || echo "Unset"
Unset

É melhor checá-lo de acordo com o tipo de variável: variável env, parâmetro ou variável regular.

Para uma variável env:

[[ $(env | grep "varname=" | wc -l) -eq 1 ]] && echo "Set" || echo "Unset"

Para um parâmetro (por exemplo, para verificar a existência do parâmetro $5):

[[ $# -ge 5 ]] && echo "Set" || echo "Unset"

Para uma variável regular (usando uma função auxiliar, para fazê-lo de maneira elegante):

function declare_var {
   declare -p "$1" &> /dev/null
}
declare_var "var_name" && echo "Set" || echo "Unset"

Notas:

  • $#: fornece o número de parâmetros posicionais.
  • declare -p: fornece a definição da variável passada como parâmetro. Se existir, retorna 0, se não, retorna 1 e imprime uma mensagem de erro.
  • &> /dev/null: suprime a saída declare -psem afetar seu código de retorno.
Peregring-lk
fonte
5

As respostas acima não funcionam quando a opção Bash set -uestá ativada. Além disso, eles não são dinâmicos, por exemplo, como testar a variável com o nome "dummy" definido? Tente o seguinte:

is_var_defined()
{
    if [ $# -ne 1 ]
    then
        echo "Expected exactly one argument: variable name as string, e.g., 'my_var'"
        exit 1
    fi
    # Tricky.  Since Bash option 'set -u' may be enabled, we cannot directly test if a variable
    # is defined with this construct: [ ! -z "$var" ].  Instead, we must use default value
    # substitution with this construct: [ ! -z "${var:-}" ].  Normally, a default value follows the
    # operator ':-', but here we leave it blank for empty (null) string.  Finally, we need to
    # substitute the text from $1 as 'var'.  This is not allowed directly in Bash with this
    # construct: [ ! -z "${$1:-}" ].  We need to use indirection with eval operator.
    # Example: $1="var"
    # Expansion for eval operator: "[ ! -z \${$1:-} ]" -> "[ ! -z \${var:-} ]"
    # Code  execute: [ ! -z ${var:-} ]
    eval "[ ! -z \${$1:-} ]"
    return $?  # Pedantic.
}

Relacionado: No Bash, como faço para testar se uma variável está definida no modo "-u"

kevinarpe
fonte
1
Eu gostaria de saber muito por que essa resposta foi rebaixada ... Funciona muito bem para mim.
LavaScornedOven
Em Bash, você poderia fazer o mesmo sem um eval: mudança eval "[ ! -z \${$1:-} ]"para avaliação indireta: [ ! -z ${!1:-} ];.
@BinaryZebra: ideia interessante. Sugiro que você publique como uma resposta separada.
Kevinarpe
O uso de "eval" torna esta solução vulnerável à injeção de código: $ is_var_defined 'x}]; eco "peguei"> & 2; # 'gotcha
tetsujin
4

Você pode fazer:

function a {
        if [ ! -z "$1" ]; then
                echo '$1 is set'
        fi
}
codaddict
fonte
9
Ou você poderia fazer -ne não negar o -z.
Pausado até novo aviso.
1
você precisa de aspas $1, ie [ ! -z "$1" ]. Ou você pode escrever [[ ! -z $1 ]]em bash / ksh / zsh.
Gilles 'SO- stop be evil' em
5
Isso falhará se $1estiver definido como a sequência vazia.
hellogoodbye
4

Minha maneira preferida é esta:

$ var=10
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
is set
$ unset -v var
$ if ! ${var+false};then echo "is set";else echo "NOT set";fi
NOT set

Então, basicamente, se uma variável é definida, ela se torna "uma negação do resultado false" (o que será true= "está definido").

E, se não estiver definido, ele se tornará "uma negação do resultado true" (conforme o resultado vazio for avaliado true) (assim terminará como sendo false= "NÃO definido").

Poder do Aquário
fonte
1
[[ $foo ]]

Ou

(( ${#foo} ))

Ou

let ${#foo}

Ou

declare -p foo
Steven Penny
fonte
1

Em um shell, você pode usar o -zoperador True, se o comprimento da string for zero.

Um one-liner simples para definir o padrão, MY_VARse não estiver definido, caso contrário, opcionalmente, você pode exibir a mensagem:

[[ -z "$MY_VAR" ]] && MY_VAR="default"
[[ -z "$MY_VAR" ]] && MY_VAR="default" || echo "Variable already set."
kenorb
fonte
0
if [[ ${1:+isset} ]]
then echo "It was set and not null." >&2
else echo "It was not set or it was null." >&2
fi

if [[ ${1+isset} ]]
then echo "It was set but might be null." >&2
else echo "It was was not set." >&2
fi
solidsnack
fonte
$1, $2e até $Nsempre está definido. Desculpe, isso é falha.
Eu tentei e não funcionou, talvez minha versão do bash não tenha suporte para isso. Idk, desculpe se estou errado. :)
0

Encontrei um código (muito) melhor para fazer isso, se você quiser verificar alguma coisa $@.

se [[$ 1 = ""]]
então
  eco '$ 1 está em branco'
outro
  eco '$ 1 está preenchido'
fi

Por que isso tudo? Tudo $@existe no Bash, mas, por padrão, está em branco, test -ze test -nnão pode ajudá-lo.

Atualização: Você também pode contar o número de caracteres em um parâmetro.

se [$ {# 1} = 0]
então
  eco '$ 1 está em branco'
outro
  eco '$ 1 está preenchido'
fi

fonte
Eu sei, mas é isso. Se algo estiver vazio, você receberá um erro! Desculpa. : P
0
if [[ ${!xx[@]} ]] ; then echo xx is defined; fi
Graham
fonte
11
Bem-vindo ao StackOverflow. Embora sua resposta seja apreciada, é melhor fornecer mais do que apenas código. Você poderia adicionar raciocínio à sua resposta ou uma pequena explicação? Consulte esta página para outras idéias sobre como expandir sua resposta.
Celeo
0

É isso que eu uso todos os dias:

#
# Check if a variable is set
#   param1  name of the variable
#
function is_set()
{
    [[ -n "${1}" ]] && test -n "$(eval "echo "\${${1}+x}"")"
}

Isso funciona bem no Linux e Solaris até o bash 3.0.

bash-3.00$ myvar="TEST"
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ mavar=""
bash-3.00$ is_set myvar ; echo $?
0
bash-3.00$ unset myvar
bash-3.00$ is_set myvar ; echo $?
1
fr00tyl00p
fonte
1
Desde Bash 2.0+ expansão indireta está disponível. Você poderia substituir a longa e complexa (com um eval também incluído) test -n "$(eval "echo "\${${1}+x}"")"para o equivalente:[[ ${!1+x} ]]
0

Eu gosto de funções auxiliares para esconder os detalhes brutos do bash. Nesse caso, fazer isso adiciona ainda mais grossura (oculta):

# The first ! negates the result (can't use -n to achieve this)
# the second ! expands the content of varname (can't do ${$varname})
function IsDeclared_Tricky
{
  local varname="$1"
  ! [ -z ${!varname+x} ]
}

Como tive bugs pela primeira vez nesta implementação (inspirada nas respostas de Jens e Lionel), criei uma solução diferente:

# Ask for the properties of the variable - fails if not declared
function IsDeclared()
{
  declare -p $1 &>/dev/null
}

Acho que é mais direto, mais bash e mais fácil de entender / lembrar. O caso de teste mostra que é equivalente:

function main()
{
  declare -i xyz
  local foo
  local bar=
  local baz=''

  IsDeclared_Tricky xyz; echo "IsDeclared_Tricky xyz: $?"
  IsDeclared_Tricky foo; echo "IsDeclared_Tricky foo: $?"
  IsDeclared_Tricky bar; echo "IsDeclared_Tricky bar: $?"
  IsDeclared_Tricky baz; echo "IsDeclared_Tricky baz: $?"

  IsDeclared xyz; echo "IsDeclared xyz: $?"
  IsDeclared foo; echo "IsDeclared foo: $?"
  IsDeclared bar; echo "IsDeclared bar: $?"
  IsDeclared baz; echo "IsDeclared baz: $?"
}

main

O caso de teste, também mostra que local varnão NÃO declaram var (a não ser seguido por '='). Por algum tempo, pensei em declarar variáveis ​​dessa maneira, apenas para descobrir agora que apenas expressei minha intenção ... Acho que não é uma operação.

IsDeclared_Tricky xyz: 1
foo IsDeclared_Tricky: 1
IsDeclared_Tricky bar: 0
IsDeclared_Tricky baz: 0
IsDeclared xyz: 1
foo IsDeclared: 1
bar IsDeclared: 0
baz IsDeclared: 0

BÔNUS: caso de uso

Eu principalmente uso esse teste para fornecer (e retornar) parâmetros para funções de uma maneira um pouco "elegante" e segura (quase semelhante a uma interface ...):

#auxiliary functions
function die()
{
  echo "Error: $1"; exit 1
}

function assertVariableDeclared()
{
  IsDeclared "$1" || die "variable not declared: $1"
}

function expectVariables()
{
  while (( $# > 0 )); do
    assertVariableDeclared $1; shift
  done
}

# actual example
function exampleFunction()
{
  expectVariables inputStr outputStr
  outputStr="$inputStr world!"
}

function bonus()
{
  local inputStr='Hello'
  local outputStr= # remove this to trigger error
  exampleFunction
  echo $outputStr
}

bonus

Se chamado com all requer variáveis ​​declaradas:

Olá Mundo!

outro:

Erro: variável não declarada: outputStr

Daniel S
fonte
0

Depois de examinar todas as respostas, isso também funciona:

if [[ -z $SOME_VAR ]]; then read -p "Enter a value for SOME_VAR: " SOME_VAR; fi
echo "SOME_VAR=$SOME_VAR"

se você não colocar no SOME_VARlugar do que eu tenho $SOME_VAR, será definido como um valor vazio; $é necessário para que isso funcione.

Hatem Jaber
fonte
não vai funcionar quando você tem set -u, com efeito, que irá imprimir o unboud variableerro
Michael Heuberger
0

Sumário

  • Use test -n "${var-}"para verificar se a variável não está vazia (e, portanto, também deve ser definida / definida)
  • Use test ! -z "${var+}"para verificar se a variável está definida / definida (mesmo que esteja vazia)

Observe que o primeiro caso de uso é muito mais comum em scripts de shell e é isso que você geralmente desejará usar.

Notas

  • Esta solução deve funcionar em todos os shells POSIX (sh, bash, zsh, ksh, dash)
  • Algumas das outras respostas para esta pergunta estão corretas, mas podem ser confusas para pessoas inexperientes em scripts de shell, então eu queria fornecer uma resposta TLDR que seja menos confusa para essas pessoas.

Explicação

Para entender como essa solução funciona, você precisa entender o comando POSIXtest e a expansão dos parâmetros do shell POSIX ( spec ), portanto, vamos cobrir o básico absoluto necessário para entender a resposta.

O comando test avalia uma expressão e retorna verdadeiro ou falso (por meio do status de saída). O operador -nretornará true se o operando for uma sequência não vazia. Por exemplo, test -n "a"retorna true, enquanto test -n ""retorna false. Agora, para verificar se uma variável não está vazia (o que significa que deve ser definida), você pode usar test -n "$var". No entanto, alguns scripts de shell têm uma opção set ( set -u) que faz com que qualquer referência a variáveis ​​indefinidas emita um erro; portanto, se a variável varnão estiver definida, a expressão $acausará um erro. Para lidar com esse caso corretamente, você deve usar a expansão de variável, que informará o shell para substituir a variável por uma sequência alternativa, se não estiver definida, evitando o erro mencionado acima.

A expansão da variável ${var-}significa: se a variável não varestiver definida (também chamada de "não definida"), substitua-a por uma sequência vazia. Portanto test -n "${var-}", retornará true se $varnão estiver vazio, o que quase sempre é o que você deseja verificar nos scripts de shell. A verificação inversa, se $varfor indefinida ou vazia, seria test -z "${var-}".

Agora, para o segundo caso de uso: verificando se a variável varestá definida, vazia ou não. Este é um caso de uso menos comum e um pouco mais complexo, e eu aconselho você a ler a ótima resposta de Lionels para entendê-la melhor.

infokiller
fonte
-1

Para verificar se um var está definido ou não

var=""; [[ $var ]] && echo "set" || echo "not set"
koola
fonte
3
Isso falhará se $varestiver definido como a sequência vazia.
hellogoodbye
-1

Se você deseja testar se uma variável está vinculada ou não, isso funciona bem, mesmo depois de ativar a opção de conjunto de substantivos:

set -o noun set

if printenv variableName >/dev/null; then
    # variable is bound to a value
else
    # variable is unbound
fi
user1857592
fonte
Eu acho que você quer dizer set -o nounset, não set -o noun set. Isso funciona apenas para variáveis ​​que foram exporteditadas. Ele também altera suas configurações de uma maneira difícil de desfazer.
Keith Thompson