Si quiero verificar la cadena nula que haría
[ -z $mystr ]
pero ¿qué pasa si quiero verificar si la variable se ha definido? ¿O no hay distinción en las secuencias de comandos Bash?
Creo que la respuesta que busca está implícita (si no se indica) en la respuesta de Vinko , aunque no se explica simplemente. Para distinguir si VAR está configurado pero vacío o no, puede 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
Probablemente pueda combinar las dos pruebas en la segunda línea en una con:
if [ -z "$VAR" -a "${VAR+xxx}" = "xxx" ]; then echo VAR is set but empty; fi
Sin embargo, si lee la documentación de Autoconf, encontrará que no recomiendan combinar términos con ' -a
' y recomiendan usar pruebas simples separadas combinadas con &&
. No he encontrado un sistema donde haya un problema; eso no significa que no existían (pero probablemente son extremadamente raros en estos días, incluso si no fueran tan raros en el pasado distante).
Puede encontrar los detalles de estas y otras expansiones de parámetros de shell relacionados , el comando test
o[
y las expresiones condicionales en el manual de Bash.
Recientemente me preguntaron por correo electrónico sobre esta respuesta con la pregunta:
Usas dos pruebas, y entiendo bien la segunda, pero no la primera. Más precisamente, no entiendo la necesidad de expansión variable
if [ -z "${VAR+xxx}" ]; then echo VAR is not set at all; fi
¿No lograría esto lo mismo?
if [ -z "${VAR}" ]; then echo VAR is not set at all; fi
Pregunta justa: la respuesta es 'No, su alternativa más simple no hace lo mismo'.
Supongamos que escribo esto antes de su prueba:
VAR=
Su prueba dirá "VAR no está configurado en absoluto", pero el mío dirá (por implicación porque no tiene eco) "VAR está configurado pero su valor puede estar vacío". Prueba 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
)
El resultado es:
JL:1 VAR is not set at all
MP:1 VAR is not set at all
MP:2 VAR is not set at all
En el segundo par de pruebas, la variable se establece, pero se establece en el valor vacío. Esta es la distinción que hacen las anotaciones ${VAR=value}
y ${VAR:=value}
. Lo mismo para ${VAR-value}
y ${VAR:-value}
, y ${VAR+value}
y ${VAR:+value}
, y así sucesivamente.
Como Gili señala en su respuesta , si corres bash
con la set -o nounset
opción, entonces la respuesta básica anterior falla unbound variable
. Se remedia fácilmente:
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
O puede cancelar la set -o nounset
opción con set +u
(que set -u
es equivalente a set -o nounset
).
${+}
y ${-}
no esté claro, pero la familiaridad con esas construcciones es esencial si se quiere ser un usuario competente del shell.
set -o nounset
habilitado.
[ -v VAR ]
enfoque " " con bash -s v4.2 y más allá .
~> 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 también funciona para variables indefinidas. Para distinguir entre un indefinido y uno definido, usaría las cosas enumeradas aquí o, con explicaciones más claras, aquí .
La forma más limpia es usar la expansión como en estos ejemplos. Para obtener todas sus opciones, consulte la sección Expansión de parámetros del manual.
Palabra alternativa:
~$ unset FOO
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
~$ FOO=""
~$ if test ${FOO+defined}; then echo "DEFINED"; fi
DEFINED
Valor por defecto:
~$ FOO=""
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
~$ unset FOO
~$ if test "${FOO-default value}" ; then echo "UNDEFINED"; fi
UNDEFINED
Por supuesto, usaría uno de estos de manera diferente, colocando el valor que desea en lugar del 'valor predeterminado' y usando la expansión directamente, si corresponde.
Guía avanzada de secuencias de comandos bash, 10.2. Sustitución de parámetros:
para basar la lógica de su programa en si la variable $ mystr está definida o no, puede hacer lo siguiente:
isdefined=0
${mystr+ export isdefined=1}
ahora, si isdefined = 0, entonces la variable no estaba definida, si isdefined = 1, la variable estaba definida
Esta forma de verificar las variables es mejor que la respuesta anterior porque es más elegante, legible, y si su shell bash se configuró para error en el uso de variables indefinidas (set -u)
, el script terminará prematuramente.
Otras cosas útiles:
tener un valor predeterminado de 7 asignado a $ mystr si no estaba definido, y dejarlo intacto de lo contrario:
mystr=${mystr- 7}
para imprimir un mensaje de error y salir de la función si la variable no está definida:
: ${mystr? not defined}
Tenga en cuenta aquí que usé ':' para no tener el contenido de $ mystr ejecutado como un comando en caso de que esté definido.
var= ; varname=var ; : ${!varname?not defined}
y esto termina: varname=var ; : ${!varname?not defined}
. Pero es un buen hábito de usar set -u
que hace lo mismo mucho más fácil.
Un resumen de las pruebas.
[ -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"
${var+x}
esto no es necesario, excepto si se permite que los nombres de variables tengan espacios en ellos.
[
obtener resultados correctos. Si no var
está configurado o está vacío, se [ -n $var ]
reduce a [ -n ]
cuál tiene el estado de salida 0, mientras que [ -n "$var" ]
tiene el estado de salida esperado (y correcto) de 1.
https://stackoverflow.com/a/9824943/14731 contiene una mejor respuesta (una que es más legible y funciona con set -o nounset
habilitada). Funciona más o menos así:
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
La forma explícita de verificar una variable que se define sería:
[ -v mystr ]
test
operador en el final de la sección Bourne Shell Builtins . No sé cuándo se agregó, pero no está en la versión de Apple bash
(basada en bash
3.2.51), por lo que probablemente sea una característica 4.x.
GNU bash, version 4.1.2(1)-release (x86_64-redhat-linux-gnu)
. El error es -bash: [: -v: unary operator expected
. Según un comentario aquí , se requiere como mínimo bash 4.2 para funcionar.
otra opción: la expansión "listar índices de matriz" :
$ unset foo
$ foo=
$ echo ${!foo[*]}
0
$ foo=bar
$ echo ${!foo[*]}
0
$ foo=(bar baz)
$ echo ${!foo[*]}
0 1
el único momento en que esto se expande a la cadena vacía es cuando no foo
está configurado, por lo que puede verificarlo con la cadena condicional:
$ unset foo
$ [[ ${!foo[*]} ]]; echo $?
1
$ foo=
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=bar
$ [[ ${!foo[*]} ]]; echo $?
0
$ foo=(bar baz)
$ [[ ${!foo[*]} ]]; echo $?
0
debería estar disponible en cualquier versión bash> = 3.0
no arrojar esta bicicleta aún más lejos, pero quería agregar
shopt -s -o nounset
es algo que podría agregar a la parte superior de una secuencia de comandos, lo que generará un error si las variables no se declaran en ninguna parte de la secuencia de comandos. El mensaje que vería es unbound variable
, pero como otros mencionan, no detectará una cadena vacía o un valor nulo. Para asegurarnos de que cualquier valor individual no esté vacío, podemos probar una variable a medida que se expande ${mystr:?}
, también conocida como expansión de signo de dólar, que generaría un error parameter null or not set
.
El Manual de referencia de Bash es una fuente autorizada de información sobre bash.
Aquí hay un ejemplo de prueba de una variable para ver si existe:
if [ -z "$PS1" ]; then
echo This shell is not interactive
else
echo This shell is interactive
fi
(De la sección 6.3.2 .)
Tenga en cuenta que el espacio en blanco después de la apertura [
y antes de la no]
es opcional .
Consejos para usuarios de Vim
Tenía un script que tenía varias declaraciones de la siguiente manera:
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
Pero quería que difirieran a cualquier valor existente. Así que los reescribí para que se vean así:
if [ -z "$VARIABLE_NAME" ]; then
export VARIABLE_NAME="$SOME_OTHER_VARIABLE/path-part"
fi
Pude automatizar esto en vim usando una expresión regular rápida:
s/\vexport ([A-Z_]+)\=("[^"]+")\n/if [ -z "$\1" ]; then\r export \1=\2\rfi\r/gc
Esto se puede aplicar seleccionando visualmente las líneas relevantes y luego escribiendo :
. La barra de comandos se rellena previamente con :'<,'>
. Pegue el comando anterior y presione enter.
Probado en esta versión de Vim:
VIM - Vi IMproved 7.3 (2010 Aug 15, compiled Aug 22 2015 15:38:58)
Compiled by root@apple.com
Los usuarios de Windows pueden querer diferentes finales de línea.
Esto es lo que creo que es una forma mucho más clara de verificar si se define una variable:
var_defined() {
local var_name=$1
set | grep "^${var_name}=" 1>/dev/null
return $?
}
Úselo de la siguiente manera:
if var_defined foo; then
echo "foo is defined"
else
echo "foo is not defined"
fi
grep
es horrible. Tenga en cuenta que bash
admite ${!var_name}
como una forma de obtener el valor de una variable cuyo nombre se especifica $var_name
, por lo que name=value; value=1; echo ${name} ${!name}
rinde value 1
.
Una versión más corta para probar una variable indefinida puede ser simplemente:
test -z ${mystr} && echo "mystr is not defined"
conjunto de llamadas sin ningún argumento ... genera todos los vars definidos ...
los últimos en la lista serían los definidos en su script ...
para que pueda canalizar su salida a algo que pueda averiguar qué cosas están definidas y cuáles no
[ -n "${VAR+x}"] && echo not null
[ -z ]
lugar de[ -z "" ]
; si tiene espacios, se convierte en[ -z "my" "test" ]
lugar de[ -z my test ]
; y si es así[ -z * ]
,*
se reemplaza por los nombres de los archivos en su directorio.