Prueba de cadena de longitud distinta de cero en Bash: [-n “$ var”] o [“$ var”]

182

He visto a los scripts de Bash probar una cadena de longitud distinta de cero de dos maneras diferentes. La mayoría de los scripts usan la -nopción:

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

Pero la opción -n no es realmente necesaria:

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

Cual es la mejor manera?

Del mismo modo, cuál es la mejor manera de probar la longitud cero:

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

o

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

Respuestas:

394

Editar: Esta es una versión más completa que muestra más diferencias entre [(aka test) y [[.

La siguiente tabla muestra que si una variable se cita o no, si usa corchetes simples o dobles y si la variable contiene solo un espacio son las cosas que afectan si el uso de una prueba con o sin -n/-zes adecuado para verificar una variable.

     | 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

Si desea saber si una variable tiene una longitud distinta de cero, realice una de las siguientes acciones:

  • cita la variable entre corchetes (columna 2a)
  • usa -ny cita la variable entre corchetes (columna 4a)
  • use corchetes dobles con o sin comillas y con o sin -n(columnas 1b - 4b)

Observe en la columna 1a que comienza en la fila etiquetada "dos" que el resultado indica que [está evaluando el contenido de la variable como si fueran parte de la expresión condicional (el resultado coincide con la aserción implícita en la "T" o "F" en la columna de descripción). Cuando [[se usa (columna 1b), el contenido variable se ve como una cadena y no se evalúa.

Los errores en las columnas 3a y 5a son causados ​​por el hecho de que el valor de la variable incluye un espacio y la variable no está entre comillas. Nuevamente, como se muestra en las columnas 3b y 5b, [[evalúa el contenido de la variable como una cadena.

En consecuencia, para las pruebas de cadenas de longitud cero, las columnas 6a, 5b y 6b muestran las formas correctas de hacerlo. También tenga en cuenta que cualquiera de estas pruebas se puede negar si la negación muestra una intención más clara que el uso de la operación opuesta. Por ejemplo: if ! [[ -n $var ]].

Si está utilizando [, la clave para asegurarse de que no obtenga resultados inesperados es citar la variable. Usando [[, no importa.

Los mensajes de error, que se están suprimiendo, son "operador unario esperado" u "operador binario esperado".

Este es el script que produjo la tabla anterior.

#!/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 hasta nuevo aviso.
fuente
1
¡Gracias! Decidí adoptar el estilo de IMO "citar la variable entre corchetes (columna 2a)", el -n solo agrega ruido y disminuye la legibilidad. Del mismo modo, para las pruebas de longitud cero o sin definir, usaré [! "$ var"] en lugar de [-z "$ var"].
AllenHalsey
2
Entonces, su gráfico para ["vs [-n"(la primera pregunta del OP) muestra que son completamente equivalentes, ¿verdad?
placas el
1
@hobs: Sí, y cuál usar depende de cuál sea más claro. También notará que su equivalente cuando se utiliza la forma de doble paréntesis que se prefiere al usar Bash.
Pausado hasta nuevo aviso.
1
Por lo tanto, para resumir su increíble tabla, la forma más clara ("mejor") de probar las cadenas no nulas es[["
hobs
22

Es mejor usar el más poderoso [[ en lo que respecta a Bash.

Casos habituales

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

Las dos construcciones anteriores se ven limpias y legibles. Deberían ser suficientes en la mayoría de los casos.

Tenga en cuenta que no necesitamos citar las expansiones variables en el interior, [[ya que no hay peligro de división de palabras y pegadura .

Para evitar las quejas de Shellcheck sobre [[ $var ]]y [[ ! $var ]], podríamos usar la -nopción.

Casos raros

En el raro caso de que tengamos que hacer una distinción entre "estar configurado en una cadena vacía" frente a "no estar configurado en absoluto", podríamos usar estos:

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

También podemos usar la -vprueba:

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

Publicaciones relacionadas y documentación

Hay muchas publicaciones relacionadas con este tema. Aquí hay algunos:

codeforester
fuente
9

Aquí hay algunas pruebas más

Verdadero si la cadena no está vacía:

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

Verdadero si la cadena está vacía:

[ -z "$var" ]
[[ -z $var ]]
test -z "$var"
! [ "$var" ]
! [[ $var ]]
! (( ${#var} ))
! let ${#var}
! test "$var"
Steven Penny
fuente
Esto no responde la pregunta de OP sobre cuál es la mejor manera.
codeforester
0

La respuesta correcta es la siguiente:

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

Tenga en cuenta el uso de [[...]], que maneja correctamente la cita de las variables por usted.

usuario2592126
fuente
2
¿Por qué usarlo -ncuando realmente no es necesario en Bash?
codeforester
0

Una forma alternativa y quizás más transparente de evaluar una variable de entorno vacía es usar ...

  if [ "x$ENV_VARIABLE" != "x" ] ; then
      echo 'ENV_VARIABLE contains something'
  fi
usuario1310789
fuente
10
Esto es muy de la vieja escuela de los días de shell Bourne. No perpetúes este viejo hábito. bashEs una herramienta más afilada que sus predecesoras.
confirmar el
2
Ni siquiera necesita esto con shells pre-bash, si está evitando la sintaxis que el estándar POSIX marca explícitamente obsoleta. [ "$ENV_VARIABLE" != "" ]funcionará en cada shell con una testimplementación compatible con POSIX , no solo bash, sino ash / dash / ksh / etc.
Charles Duffy
... es decir, no use -ao -ocombine pruebas, sino que use [ ... ] && [ ... ]o [ ... ] || [ ... ]y los únicos casos de esquina que pueden aplicarse al uso de datos de variables arbitrarias en una prueba están cerrados sin ambigüedades.
Charles Duffy
-2

Use case/esacpara probar:

case "$var" in
  "") echo "zero length";;
esac
ghostdog74
fuente
12
No por favor. Esto no casees para lo que está destinado.
tbc0
2
casefunciona mejor cuando hay más de dos alternativas.
codeforester
caseno está destinado a este tipo de uso.
Luis Lavaire