Pergunta

Se eu quiser verificar a string nula, eu faria

[ -z $mystr ]

mas e se eu quiser verificar se a variável foi definida?Ou não há distinção nos scripts bash?

Foi útil?

Solução

Eu acho que a resposta que você procura está implícita (se não declarada) por Vinko's responda, embora não esteja explicado de maneira simples. Para distinguir se o VAR está definido, mas está vazio ou não, você pode usar:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "$VAR" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Você provavelmente pode combinar os dois testes na segunda linha em um com:

if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

No entanto, se você ler a documentação para o AutoConf, você descobrirá que eles não recomendam combinar termos com '-a'e recomendo usar testes simples separados combinados com &&. Não encontrei um sistema onde há um problema; Isso não significa que eles não costumavam existir (mas provavelmente são extremamente raros hoje em dia, mesmo que não fossem tão raros no passado distante).

Você pode encontrar os detalhes desses e outros relacionados Expansões de parâmetros de shell, a test ou [ comando e expressões condicionais no manual da festa.


Recentemente, fui perguntado por e -mail sobre esta resposta com a pergunta:

Você usa dois testes e eu entendo o segundo bem, mas não o primeiro. Mais precisamente não entendo a necessidade de expansão variável

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi

Isso não conseguiria o mesmo?

if [ -z "${VAR}" ]; then echo VAR is not set at all; fi

Pergunta justa - a resposta é 'não, sua alternativa mais simples não faz a mesma coisa'.

Suponha que eu escreva isso antes do seu teste:

VAR=

Seu teste dirá "VAR não está definido", mas o meu dirá (por implicação porque ecoa nada) "VAR está definido, mas seu valor pode estar vazio". Experimente este script:

(
unset VAR
if [ -z "${VAR+xxx}" ]; then echo JL:1 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:1 VAR is not set at all; fi
VAR=
if [ -z "${VAR+xxx}" ]; then echo JL:2 VAR is not set at all; fi
if [ -z "${VAR}" ];     then echo MP:2 VAR is not set at all; fi
)

A saída é:

JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all

No segundo par de testes, a variável é definida, mas está definida como o valor vazio. Esta é a distinção de que o ${VAR=value} e ${VAR:=value} Notações fazem. Idem para ${VAR-value} e ${VAR:-value}, e ${VAR+value} e ${VAR:+value}, e assim por diante.


Como Gili aponta em seu responda, se você correr bash com o set -o nounset opção, então a resposta básica acima falha com unbound variable. É facilmente remediado:

if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
if [ -z "${VAR-}" ] && [ "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi

Ou você pode cancelar o set -o nounset opção com set +u (set -u sendo equivalente a set -o nounset).

Outras dicas

~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO=""
~> if [ -z $FOO ]; then echo "EMPTY"; fi
EMPTY
~> FOO="a"
~> if [ -z $FOO ]; then echo "EMPTY"; fi
~> 

-z também funciona para variáveis ​​​​indefinidas.Para distinguir entre um indefinido e um definido, você usaria os itens listados aqui ou, com explicações mais claras, aqui.

A maneira mais limpa é usar expansão como nestes exemplos.Para obter todas as suas opções, verifique a seção Expansão de parâmetros do manual.

Palavra alternativa:

~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED

Valor padrão:

~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED

É claro que você usaria um desses de forma diferente, colocando o valor desejado em vez do 'valor padrão' e usando a expansão diretamente, se apropriado.

Guia avançado de script bash, 10.2.Substituição de parâmetros:

  • ${var+bláblá}:Se o Var for definido, 'Blahblah' é substituído pela expressão, senão nulo é substituído
  • ${var-blahblah}:Se o Var for definido, ele próprio é substituído, senão 'Blahblah' é substituído
  • ${var?blahblah}:Se o VAR for definido, é substituído, caso contrário, a função existe com 'Blahblah' como uma mensagem de erro.


para basear a lógica do seu programa no fato de a variável $mystr estar definida ou não, você pode fazer o seguinte:

isdefined=0
${mystr+ export isdefined=1}

agora, se isdefinido=0 então a variável era indefinida, se isdefinido=1 a variável foi definida

Esta forma de verificar variáveis ​​​​é melhor que a resposta acima porque é mais elegante, legível e se o seu shell bash foi configurado com erro no uso de variáveis ​​​​indefinidas (set -u), o script será encerrado prematuramente.


Outras coisas úteis:

ter um valor padrão de 7 atribuído a $mystr se ele for indefinido e deixá-lo intacto caso contrário:

mystr=${mystr- 7}

para imprimir uma mensagem de erro e sair da função se a variável for indefinida:

: ${mystr? not defined}

Cuidado aqui que usei ':' para não ter o conteúdo de $mystr executado como comando caso esteja definido.

Um resumo dos testes.

[ -n "$var" ] && echo "var is set and not empty"
[ -z "$var" ] && echo "var is unset or empty"
[ "${var+x}" = "x" ] && echo "var is set"  # may or may not be empty
[ -n "${var+x}" ] && echo "var is set"  # may or may not be empty
[ -z "${var+x}" ] && echo "var is unset"
[ -z "${var-x}" ] && echo "var is set and empty"

https://stackoverflow.com/a/9824943/14731 contém uma resposta melhor (que é mais legível e trabalha com set -o nounset ativado). Funciona aproximadamente assim:

if [ -n "${VAR-}" ]; then
    echo "VAR is set and is not empty"
elif [ "${VAR+DEFINED_BUT_EMPTY}" = "DEFINED_BUT_EMPTY" ]; then
    echo "VAR is set, but empty"
else
    echo "VAR is not set"
fi

A maneira explícita de verificar se uma variável está sendo definida seria:

[ -v mystr ]

outra opção: A expansão "Lista Array Indices":

$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1

A única vez que isso se expande para a sequência vazia é quando foo não é definido, para que você possa verificar com a string condicional:

$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0

deve estar disponível em qualquer versão do Bash> = 3.0

para não largar ainda mais esta bicicleta, mas queria adicionar

shopt -s -o nounset

é algo que você pode adicionar ao topo de um script, que irá gerar um erro se as variáveis ​​não forem declarado em qualquer lugar do script.A mensagem que você veria é unbound variable, mas como outros mencionam, não capturará uma string vazia ou um valor nulo.Para garantir que qualquer valor individual não esteja vazio, podemos testar uma variável à medida que ela é expandida com ${mystr:?}, também conhecida como expansão do cifrão, o que causaria erro parameter null or not set.

o Manual de referência Bash é uma fonte autorizada de informações sobre Bash.

Aqui está um exemplo de teste de uma variável para ver se existe:

if [ -z "$PS1" ]; then
        echo This shell is not interactive
else
        echo This shell is interactive
fi

(A partir de Seção 6.3.2.)

Observe que o espaço em branco após o aberto [ e antes do ] é não opcional.


Dicas para usuários do VIM

Eu tinha um script que tinha várias declarações da seguinte maneira:

export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"

Mas eu queria que eles adiam para quaisquer valores existentes. Então eu reescrevi-os para ficar assim:

if [ -z "$VARIABLE_NAME" ]; then
        export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi

Consegui automatizar isso em vim usando um regex rápido:

s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r  export \1=\2\rfi\r/gc

Isso pode ser aplicado selecionando as linhas relevantes visualmente e digitando :. A barra de comando pré-popula com :'<,'>. Cole o comando acima e pressione Enter.


Testado nesta versão do vim:

VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com

Os usuários do Windows podem querer terminações de linha diferentes.

Aqui está o que eu acho que é uma maneira muito mais clara de verificar se uma variável está definida:

var_defined() {
    local var_name=$1
    set | grep "^${var_name}=" 1>/dev/null
    return $?
}

Use o seguinte:

if var_defined foo; then
    echo "foo is defined"
else
    echo "foo is not defined"
fi

Conjunto de chamadas sem argumentos. Ele gera todos os vars definidos.
Os últimos da lista seriam os definidos em seu script.
Então você pode colocar sua saída para algo que poderia descobrir o que as coisas são definidas e o que não

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top