Teste para uma sequência de comprimento diferente de zero no Bash: [-n "$ var"] ou ["$ var"]

182

Eu vi scripts Bash testarem uma cadeia de comprimento diferente de zero de duas maneiras diferentes. A maioria dos scripts usa a -nopção:

#!/bin/bash
# With the -n option
if [ -n "$var" ]; then
  # Do something when var is non-zero length
fi

Mas a opção -n não é realmente necessária:

# Without the -n option
if [ "$var" ]; then
  # Do something when var is non-zero length
fi

Qual é o melhor caminho?

Da mesma forma, qual é a melhor maneira de testar comprimento zero:

if [ -z "$var" ]; then
  # Do something when var is zero-length
fi

ou

if [ ! "$var" ]; then
  # Do something when var is zero-length
fi
AllenHalsey
fonte

Respostas:

394

Editar: esta é uma versão mais completa que mostra mais diferenças entre [(aka test) e [[.

A tabela a seguir mostra que, se uma variável é citada ou não, se você usa colchetes simples ou duplos e se a variável contém apenas um espaço, isso afeta o fato de o uso de um teste com ou sem -n/-zser adequado para verificar uma variável.

     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b
     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"
-----+------------------------------------+------------------------------------
unset| false false true  false true  true | false false false false true  true
null | false false true  false true  true | false false false false true  true
space| false true  true  true  true  false| true  true  true  true  false false
zero | true  true  true  true  false false| true  true  true  true  false false
digit| true  true  true  true  false false| true  true  true  true  false false
char | true  true  true  true  false false| true  true  true  true  false false
hyphn| true  true  true  true  false false| true  true  true  true  false false
two  | -err- true  -err- true  -err- false| true  true  true  true  false false
part | -err- true  -err- true  -err- false| true  true  true  true  false false
Tstr | true  true  -err- true  -err- false| true  true  true  true  false false
Fsym | false true  -err- true  -err- false| true  true  true  true  false false
T=   | true  true  -err- true  -err- false| true  true  true  true  false false
F=   | false true  -err- true  -err- false| true  true  true  true  false false
T!=  | true  true  -err- true  -err- false| true  true  true  true  false false
F!=  | false true  -err- true  -err- false| true  true  true  true  false false
Teq  | true  true  -err- true  -err- false| true  true  true  true  false false
Feq  | false true  -err- true  -err- false| true  true  true  true  false false
Tne  | true  true  -err- true  -err- false| true  true  true  true  false false
Fne  | false true  -err- true  -err- false| true  true  true  true  false false

Se você deseja saber se uma variável tem comprimento diferente de zero, siga um destes procedimentos:

  • cite a variável entre colchetes (coluna 2a)
  • use -ne cite a variável entre colchetes (coluna 4a)
  • use colchetes duplos com ou sem aspas e com ou sem -n(colunas 1b - 4b)

Observe na coluna 1a, começando na linha rotulada "dois", que o resultado indica que [está avaliando o conteúdo da variável como se eles fizessem parte da expressão condicional (o resultado corresponde à afirmação implícita no "T" ou "F" em a coluna de descrição). Quando [[é usado (coluna 1b), o conteúdo da variável é visto como uma sequência e não é avaliado.

Os erros nas colunas 3a e 5a são causados ​​pelo fato de o valor da variável incluir um espaço e a variável não estar entre aspas. Novamente, como mostrado nas colunas 3b e 5b, [[avalia o conteúdo da variável como uma sequência.

Da mesma forma, para testes para cadeias de comprimento zero, as colunas 6a, 5b e 6b mostram as maneiras corretas de fazer isso. Observe também que qualquer um desses testes pode ser negado se negar mostrar uma intenção mais clara do que usar a operação oposta. Por exemplo: if ! [[ -n $var ]].

Se você estiver usando [, a chave para garantir que você não obtenha resultados inesperados é citar a variável. Usando [[, isso não importa.

As mensagens de erro que estão sendo suprimidas são "operador unário esperado" ou "operador binário esperado".

Este é o script que produziu a tabela acima.

#!/bin/bash
# by Dennis Williamson
# 2010-10-06, revised 2010-11-10
# for http://stackoverflow.com/q/3869072
# designed to fit an 80 character terminal

dw=5    # description column width
w=6     # table column width

t () { printf '%-*s' "$w" " true"; }
f () { [[ $? == 1 ]] && printf '%-*s' "$w" " false" || printf '%-*s' "$w" " -err-"; }

o=/dev/null

echo '     | 1a    2a    3a    4a    5a    6a   | 1b    2b    3b    4b    5b    6b'
echo '     | [     ["    [-n   [-n"  [-z   [-z" | [[    [["   [[-n  [[-n" [[-z  [[-z"'
echo '-----+------------------------------------+------------------------------------'

while read -r d t
do
    printf '%-*s|' "$dw" "$d"

    case $d in
        unset) unset t  ;;
        space) t=' '    ;;
    esac

    [ $t ]        2>$o  && t || f
    [ "$t" ]            && t || f
    [ -n $t ]     2>$o  && t || f
    [ -n "$t" ]         && t || f
    [ -z $t ]     2>$o  && t || f
    [ -z "$t" ]         && t || f
    echo -n "|"
    [[ $t ]]            && t || f
    [[ "$t" ]]          && t || f
    [[ -n $t ]]         && t || f
    [[ -n "$t" ]]       && t || f
    [[ -z $t ]]         && t || f
    [[ -z "$t" ]]       && t || f
    echo

done <<'EOF'
unset
null
space
zero    0
digit   1
char    c
hyphn   -z
two     a b
part    a -a
Tstr    -n a
Fsym    -h .
T=      1 = 1
F=      1 = 2
T!=     1 != 2
F!=     1 != 1
Teq     1 -eq 1
Feq     1 -eq 2
Tne     1 -ne 2
Fne     1 -ne 1
EOF
Pausado até novo aviso.
fonte
1
Obrigado! Decidi adotar o estilo de "citar a variável entre colchetes (coluna 2a)" IMO, o -n apenas adiciona ruído e diminui a legibilidade. Da mesma forma, para testar comprimento zero ou não definido, usarei [! "$ var"] em vez de [-z "$ var"].
AllenHalsey
2
Então, seu gráfico para ["vs [-n"(a primeira pergunta do OP) mostra que eles são completamente equivalentes, certo?
Hj
1
@ hobs: Sim, e qual usar depende de qual é mais claro. Você também notará que eles são equivalentes ao usar o formulário de colchete duplo, que é preferido ao usar o Bash.
Pausado até novo aviso.
1
Portanto, para resumir sua tabela impressionante, a maneira mais clara ("melhor") de testar cadeias não nulas é[["
hobs
22

É melhor usar o mais poderoso no [[ que diz respeito ao Bash.

Casos usuais

if [[ $var ]]; then   # var is set and it is not empty
if [[ ! $var ]]; then # var is not set or it is set to an empty string

As duas construções acima parecem limpas e legíveis. Eles devem bastar na maioria dos casos.

Observe que não precisamos citar as expansões variáveis ​​internas, [[pois não há perigo de divisão e globbing de palavras .

Para evitar queixas macias do shellcheck sobre [[ $var ]]e [[ ! $var ]], poderíamos usar a -nopção

Casos raros

No raro caso de termos que fazer uma distinção entre "ser definido como uma cadeia vazia" vs "não ser definido", poderíamos usá-los:

if [[ ${var+x} ]]; then           # var is set but it could be empty
if [[ ! ${var+x} ]]; then         # var is not set
if [[ ${var+x} && ! $var ]]; then # var is set and is empty

Também podemos usar o -vteste:

if [[ -v var ]]; then             # var is set but it could be empty
if [[ ! -v var ]]; then           # var is not set
if [[ -v var && ! $var ]]; then   # var is set and is empty
if [[ -v var && -z $var ]]; then  # var is set and is empty

Publicações e documentação relacionadas

Há muitas postagens relacionadas a esse tópico. Aqui estão alguns:

codeforester
fonte
9

Aqui estão mais alguns testes

Verdadeiro se a sequência não estiver vazia:

[ -n "$var" ]
[[ -n $var ]]
test -n "$var"
[ "$var" ]
[[ $var ]]
(( ${#var} ))
let ${#var}
test "$var"

Verdadeiro se a sequência estiver vazia:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"
Steven Penny
fonte
Isso não responde à pergunta da OP sobre qual é a melhor maneira.
código é o seguinte
0

A resposta correta é a seguinte:

if [[ -n $var ]] ; then
  blah
fi

Observe o uso de [[...]], que manipula corretamente citando as variáveis ​​para você.

user2592126
fonte
2
Por que usar -nquando não é realmente necessário no Bash?
precisa saber é o seguinte
0

Uma maneira alternativa e talvez mais transparente de avaliar uma variável de ambiente vazia é usar ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
user1310789
fonte
10
Isso é muito antiquado, desde os dias de Bourne. Não perpetue esse velho hábito. bashé uma ferramenta mais nítida que seus antecessores.
tbc0
2
Você nem precisa disso com shells pré-bash, se estiver evitando a sintaxe que o padrão POSIX marca explicitamente como obsoleto. [ "$ENV_VARIABLE" != "" ]funcionará em todos os shell com uma testimplementação compatível com POSIX - não apenas o bash, mas o ash / dash / ksh / etc.
Charles Duffy
... ou seja, não use -aou -ocombine testes, mas use [ ... ] && [ ... ]or [ ... ] || [ ... ]e os únicos casos de canto que podem ser aplicados ao uso de dados de variáveis ​​arbitrárias em um teste são fechados sem ambiguidade.
Charles Duffy
-2

Use case/esacpara testar:

case "$var" in
  "") echo "zero length";;
esac
ghostdog74
fonte
12
Não por favor. Não é para isso que caseserve.
tbc0
2
casefunciona melhor quando há mais de duas alternativas.
precisa saber é o seguinte
casenão se destina a esse tipo de uso.
Luis Lavaire 27/05/19