Nota
Estoy dando una respuesta muy centrada en Bash debido a la bash
etiqueta.
Respuesta corta
Siempre que solo se trate de variables con nombre en Bash, esta función siempre debería decirle si la variable se ha configurado, incluso si se trata de una matriz vacía.
variable-is-set() {
declare -p "$1" &>/dev/null
}
Por que esto funciona
En Bash (al menos desde 3.0), si var
es una variable declarada / establecida, declare -p var
genera un declare
comando que establecería la variable var
sea cual sea su tipo y valor actuales, y devuelve el código de estado 0
(éxito). Si var
no está declarado, declare -p var
envía un mensaje de error stderr
y devuelve el código de estado 1
. Usando &>/dev/null
, redirige tanto regular stdout
comostderr
salida a/dev/null
, nunca se verá, y sin cambiar el código de estado. Por lo tanto, la función solo devuelve el código de estado.
¿Por qué otros métodos (a veces) fallan en Bash
[ -n "$var" ]
: Esto solo comprueba si ${var[0]}
no está vacío. (En Bash, $var
es lo mismo que ${var[0]}
).
[ -n "${var+x}" ]
: Esto solo verifica si ${var[0]}
está configurado.
[ "${#var[@]}" != 0 ]
: Esto solo comprueba si se establece al menos un índice de $var
.
Cuando este método falla en Bash
Esto sólo funciona para las variables con nombre (incluidos $_
), no ciertas variables especiales ( $!
, $@
, $#
, $$
, $*
, $?
, $-
, $0
, $1
, $2
, ..., y ninguno de los que puede haber olvidado). Como ninguno de estos son matrices, el estilo POSIX [ -n "${var+x}" ]
funciona para todas estas variables especiales. Pero tenga cuidado de incluirlo en una función ya que muchas variables especiales cambian los valores / existencia cuando se llaman funciones.
Nota de compatibilidad de shell
Si su script tiene matrices y está tratando de hacerlo compatible con la mayor cantidad posible de shells, entonces considere usar en typeset -p
lugar de declare -p
. He leído que ksh solo es compatible con el primero, pero no he podido probar esto. Sé que Bash 3.0+ y Zsh 5.5.1 son compatibles con ambos typeset -p
y declare -p
solo difieren en cuál es una alternativa para el otro. Pero no he probado las diferencias más allá de esas dos palabras clave, y no he probado otros shells.
Si necesita que su script sea compatible con POSIX sh, no puede usar matrices. Sin matrices, [ -n "{$var+x}" ]
funciona.
Código de comparación para diferentes métodos en Bash
Esta función desarma la variable var
, eval
s el código pasado, ejecuta pruebas para determinar si var
está establecida por el eval
código d, y finalmente muestra los códigos de estado resultantes para las diferentes pruebas.
Estoy omitiendo test -v var
, [ -v var ]
y [[ -v var ]]
porque producen resultados idénticos al estándar POSIX [ -n "${var+x}" ]
, al tiempo que requieren Bash 4.2+. También estoy omitiendo typeset -p
porque es lo mismo que declare -p
en los shells que he probado (Bash 3.0 a 5.0 y 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 de caso de prueba
Tenga en cuenta que los resultados de la prueba pueden ser inesperados debido a que Bash trata los índices de matriz no numéricos como "0" si la variable no se ha declarado como una matriz asociativa. Además, las matrices asociativas solo son válidas en 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
Prueba de salida
Los mnemónicos de ensayo en la fila de cabecera corresponden a [ -n "$var" ]
, [ -n "${var+x}" ]
, [ "${#var[@]}" != 0 ]
, y 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
Resumen
declare -p var &>/dev/null
es (100%?) confiable para probar variables con nombre en Bash desde al menos 3.0.
[ -n "${var+x}" ]
es confiable en situaciones compatibles con POSIX, pero no puede manejar matrices.
- Existen otras pruebas para verificar si una variable no está vacía y para verificar las variables declaradas en otros shells. Pero estas pruebas no son adecuadas para las secuencias de comandos Bash ni POSIX.
if test $# -gt 0; then printf 'arg <%s>\n' "$@"; fi
.