Estoy escribiendo un script de bash que tiene set -u
, y tengo un problema con la expansión de matriz vacía: bash parece tratar una matriz vacía como una variable no configurada durante la expansión:
$ set -u
$ arr=()
$ echo "foo: '${arr[@]}'"
bash: arr[@]: unbound variable
( declare -a arr
tampoco ayuda)
Una solución común a esto es usar ${arr[@]-}
en su lugar, sustituyendo así una cadena vacía en lugar de la matriz vacía ("indefinida"). Sin embargo, esta no es una buena solución, ya que ahora no puede distinguir entre una matriz con una sola cadena vacía y una matriz vacía. (@ -Ampliación es especial en bash, se expande "${arr[@]}"
en "${arr[0]}" "${arr[1]}" …
, lo que hace que sea una herramienta perfecta para la construcción de líneas de comando.)
$ countArgs() { echo $#; }
$ countArgs a b c
3
$ countArgs
0
$ countArgs ""
1
$ brr=("")
$ countArgs "${brr[@]}"
1
$ countArgs "${arr[@]-}"
1
$ countArgs "${arr[@]}"
bash: arr[@]: unbound variable
$ set +u
$ countArgs "${arr[@]}"
0
Entonces, ¿hay alguna forma de solucionar ese problema, además de verificar la longitud de una matriz en un if
(ver ejemplo de código a continuación), o desactivar la -u
configuración para esa pieza corta?
if [ "${#arr[@]}" = 0 ]; then
veryLongCommandLine
else
veryLongCommandLine "${arr[@]}"
fi
Actualización:bugs
etiqueta eliminada debido a la explicación de ikegami.
"${arr[@]}"
. ¿Me estoy perdiendo de algo? Por lo que puedo ver, funciona al menos en5.x
.Según la documentación,
No se ha asignado un valor a ningún subíndice, por lo que la matriz no está configurada.
Pero aunque la documentación sugiere que aquí es apropiado un error, este ya no es el caso desde 4.4 .
Hay un condicional que puede usar en línea para lograr lo que desea en versiones anteriores: use en
${arr[@]+"${arr[@]}"}
lugar de"${arr[@]}"
.Probado con bash 4.2.25 y 4.3.11.
fuente
[@]+
realmente hace y por qué el segundo${arr[@]}
no causará un error desatado.${parameter+word}
solo se expandeword
siparameter
no está desarmado.${arr+"${arr[@]}"}
es más corto y parece funcionar igual de bien.unset arr
,arr[1]=a
,args ${arr+"${arr[@]}"}
Vsargs ${arr[@]+"${arr[@]}"}
+
expansión no ocurre (es decir, una matriz vacía), la expansión se reemplaza por nada , que es exactamente a lo que se expande una matriz vacía.:+
no es seguro porque también trata una('')
matriz de un solo elemento como no configurada y de manera similar se expande a nada, perdiendo el valor.¡La respuesta aceptada de @ikegami es sutilmente incorrecta! El encantamiento correcto es
${arr[@]+"${arr[@]}"}
:fuente
bash-4.4.23
:arr=('') && countArgs "${arr[@]:+${arr[@]}}"
produce1
. Pero la${arr[@]+"${arr[@]}"}
forma permite diferenciar entre valor vacío / no vacío al agregar / no agregar dos puntos.arr=('') && countArgs ${arr[@]:+"${arr[@]}"}
->0
,arr=('') && countArgs ${arr[@]+"${arr[@]}"}
->1
.Resulta que el manejo de matrices ha sido cambiado en bash 4.4 recientemente lanzado (2016/09/16) (disponible en Debian stretch, por ejemplo).
Ahora la expansión de matrices vacías no emite advertencia
fuente
bash-4.4.12
"${arr[@]}"
sería suficiente.esta puede ser otra opción para aquellos que prefieren no duplicar arr [@] y están bien tener una cadena vacía
Probar:
fuente
for
esto terminaría con una sola cadena vacía cuando la matriz no esté definida / definida como vacía, donde, como podría querer, el cuerpo del bucle para que no se ejecute si la matriz no está definida.${arr[@]+"${arr[@]}"}
conserva correctamente el estado de matriz vacía.La respuesta de @ikegami es correcta, pero considero que la sintaxis es
${arr[@]+"${arr[@]}"}
terrible. Si usa nombres de variables de matriz largos, comenzará a verse más rápido de lo habitual.Prueba esto en su lugar:
Parece que el operador de corte de matriz Bash es muy indulgente.
Entonces, ¿por qué Bash hizo que el manejo del caso extremo de las matrices fuera tan difícil? Suspiro. No puedo garantizar que su versión permita tal abuso del operador de corte de matriz, pero funciona a la perfección para mí.
Advertencia: estoy usando
GNU bash, version 3.2.25(1)-release (x86_64-redhat-linux-gnu)
Su kilometraje puede variar.fuente
"${arr[@]:0}"
da-bash: arr[@]: unbound variable
.arr=("_dummy_")
y usar la expansión en${arr[@]:1}
todas partes. Esto se menciona en otras respuestas, refiriéndose a los valores centinela.Incoherencia "interesante" de hecho.
Además,
Si bien estoy de acuerdo en que el comportamiento actual puede no ser un error en el sentido que explica @ikegami, en mi opinión podríamos decir que el error está en la definición (de "conjunto") en sí, y / o en el hecho de que se aplica de manera inconsistente. El párrafo anterior en la página del manual dice
que es totalmente coherente con lo que dice sobre la expansión de los parámetros posicionales en
"$@"
. No es que no haya otras inconsistencias en el comportamiento de las matrices y los parámetros posicionales ... pero para mí no hay indicios de que este detalle deba ser inconsistente entre los dos.Continuo,
Entonces,
arr[]
¿no es tan independiente que no podemos obtener un recuento de sus elementos (0) o una lista (vacía) de sus claves? Para mí, estos son razonables y útiles; el único valor atípico parece ser la${arr[@]}
(y${arr[*]}
) expansión.fuente
Estoy complementando las respuestas de @ ikegami (aceptadas) y de @ kevinarpe (también buenas).
Puede hacer
"${arr[@]:+${arr[@]}}"
para solucionar el problema. El lado derecho (es decir, después:+
) proporciona una expresión que se utilizará en caso de que el lado izquierdo no esté definido / nulo.La sintaxis es misteriosa. Tenga en cuenta que el lado derecho de la expresión sufrirá una expansión de parámetros, por lo que se debe prestar especial atención a tener citas consistentes.
Como menciona @kevinarpe, una sintaxis menos arcana es usar la notación de corte de matriz
${arr[@]:0}
(en las versiones Bash>= 4.4
), que se expande a todos los parámetros, comenzando desde el índice 0. Tampoco requiere tanta repetición. Esta expansión funciona independientemente deset -u
, por lo que puede usarla en todo momento. La página del manual dice (en Expansión de parámetros ):Este es el ejemplo proporcionado por @kevinarpe, con formato alternativo para poner la salida en evidencia:
Este comportamiento varía con las versiones de Bash. También puede haber notado que el operador de longitud
${#arr[@]}
siempre evaluará0
para matrices vacías, independientemente deset -u
, sin causar un 'error de variable no vinculada'.fuente
:0
idioma falla en Bash 4.2, por lo que este no es un enfoque seguro. Mira mi respuesta .Aquí hay un par de formas de hacer algo como esto, una usando centinelas y otra usando anexos condicionales:
fuente
Incoherencia interesante; esto le permite definir algo que "no se considera configurado" pero que aparece en la salida de
declare -p
ACTUALIZACIÓN: como mencionaron otros, se corrigió en 4.4 lanzado después de que se publicó esta respuesta.
fuente
echo ${arr[@]}
(pero antes de Bash 4.4 todavía verá un error).echo $arr[@]
usted mismo, habría visto que el mensaje de error es diferente.La forma más sencilla y compatible parece ser:
fuente