¿Hay alguna manera de imprimir una matriz completa ([clave]=valor) sin recorrer todos los elementos?
Supongamos que he creado una matriz con algunos elementos:
declare -A array
array=([a1]=1 [a2]=2 ... [b1]=bbb ... [f500]=abcdef)
Puedo imprimir toda la matriz con
for i in "${!array[@]}"
do
echo "${i}=${array[$i]}"
done
Sin embargo, parece que bash ya sabe cómo obtener todos los elementos de la matriz de una sola vez, tanto claves ${!array[@]}
como valores ${array[@]}
.
¿Hay alguna manera de hacer que bash imprima esta información sin el bucle?
Editar:
typeset -p array
¡hace eso!
Sin embargo, no puedo eliminar tanto el prefijo como el sufijo en una sola sustitución:
a="$(typeset -p array)"
b="${a##*(}"
c="${b%% )*}"
¿Existe una forma más limpia de obtener/imprimir solo la parte clave=valor del resultado?
Respuesta1
Creo que estás preguntando dos cosas diferentes.
¿Hay alguna manera de hacer que bash imprima esta información sin el bucle?
Sí, pero no son tan buenos como simplemente usar el bucle.
¿Existe una forma más limpia de obtener/imprimir solo la parte clave=valor del resultado?
Sí, el for
bucle. Tiene las ventajas de que no requiere programas externos, es sencillo y facilita el control del formato de salida exacto sin sorpresas.
Cualquier solución que intente manejar la salida de declare -p
( typeset -p
) tiene que lidiar con a) la posibilidad de que las variables mismas contengan paréntesis o corchetes, b) las comillas que declare -p
deben agregarse para que su salida sea una entrada válida para el shell.
Por ejemplo, su expansión b="${a##*(}"
consume algunos de los valores, si alguna clave/valor contiene un paréntesis de apertura. Esto se debe a que usaste ##
, que elimina elmás largoprefijo. Igual por c="${b%% )*}"
. Aunque, por supuesto, podría hacer coincidir el texto estándar impreso con declare
mayor exactitud, todavía le resultaría difícil si no quisiera todas las citas que incluye.
Esto no se ve muy bien a menos que lo necesites.
$ declare -A array=([abc]="'foobar'" [def]='"foo bar"')
$ declare -p array
declare -A array='([def]="\"foo bar\"" [abc]="'\''foobar'\''" )'
Con el for
bucle, es más fácil elegir el formato de salida que desees:
# without quoting
$ for x in "${!array[@]}"; do printf "[%s]=%s\n" "$x" "${array[$x]}" ; done
[def]="foo bar"
[abc]='foobar'
# with quoting
$ for x in "${!array[@]}"; do printf "[%q]=%q\n" "$x" "${array[$x]}" ; done
[def]=\"foo\ bar\"
[abc]=\'foobar\'
A partir de ahí también essimplepara cambiar el formato de salida de otra manera (elimine los corchetes alrededor de la clave, coloque todos los pares clave/valor en una sola línea...). Si necesita una cotización para algo que no sea el propio shell, igual tendrá que hacerlo usted mismo, pero al menos tendrá los datos sin procesar con los que trabajar. (Si tiene nuevas líneas en las claves o valores, probablemente necesitará algunas comillas).
Con un Bash actual (creo que 4.4), también puedes usar printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
en lugar de printf "%q=%q"
. Produce un formato de cita algo más agradable, pero, por supuesto, es un poco más complicado recordar escribirlo. (Y cita el caso de la esquina de @
como clave de matriz, que %q
no cita).
Si el bucle for parece demasiado cansado para escribirlo, guárdelo como función en algún lugar (sin citarlo aquí):
printarr() { declare -n __p="$1"; for k in "${!__p[@]}"; do printf "%s=%s\n" "$k" "${__p[$k]}" ; done ; }
Y luego solo usa eso:
$ declare -A a=([a]=123 [b]="foo bar" [c]="(blah)")
$ printarr a
a=123
b=foo bar
c=(blah)
También funciona con matrices indexadas:
$ b=(abba acdc)
$ printarr b
0=abba
1=acdc
Respuesta2
declare -p array
declare -A array='([a2]="2" [a1]="1" [zz]="Hello World" [b1]="bbb" [f50]="abcd" )'
2 tenedor
Tal vez esto:
printf "%s\n" "${!array[@]}"
a2
a1
f50
zz
b1
printf "%s\n" "${array[@]}"
2
1
abcd
Hello World
bbb
printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t
a2 2
a1 1
f50 abcd
zz Hello World
b1 bbb
3 tenedores
o esto:
paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}")
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
sin tenedor
ser comparado con
for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done
a2=2
a1=1
f50=abcd
zz=Hello World
b1=bbb
Comparación de tiempos de ejecución
Como la última sintaxis no usa fork, podrían ser más rápidos:
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
5 11 76
real 0m0.005s
user 0m0.000s
sys 0m0.000s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
5 6 41
real 0m0.008s
user 0m0.000s
sys 0m0.000s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
5 6 41
real 0m0.002s
user 0m0.000s
sys 0m0.001s
Pero esta afirmación no sigue siendo cierta si la matriz se vuelve grande; Si reducir las horquillas es eficiente para procesos pequeños, usar herramientas dedicadas es más eficiente para procesos más grandes.
for i in {a..z}{a..z}{a..z};do array[$i]=$RANDOM;done
time printf "%s\n" "${!array[@]}" "${array[@]}" | pr -2t | wc
17581 35163 292941
real 0m0.150s
user 0m0.124s
sys 0m0.036s
time paste -d= <(printf "%s\n" "${!array[@]}") <(printf "%s\n" "${array[@]}") | wc
17581 17582 169875
real 0m0.140s
user 0m0.000s
sys 0m0.004s
time for i in "${!array[@]}";do printf "%s=%s\n" "$i" "${array[$i]}";done | wc
17581 17582 169875
real 0m0.312s
user 0m0.268s
sys 0m0.076s
Observación
Como ambos (ahorquillado) uso de solucionesalineación, ninguno de ellos funcionará si alguna variable contiene unnueva línea. En este caso, la única forma es un for
bucle.
Respuesta más sólida y detallada en StackOverflow
Respuesta3
Bash 5.1 permite una forma muy sencilla de mostrar matrices asociativas utilizando el K
valor como en ${arr[@]@K}
:
$ declare -A arr
$ arr=(k1 v1 k2 v2)
$ printf "%s\n" "${arr[@]@K}"
k1 "v1" k2 "v2"
Desde elDocumento de descripción de Bash 5.1:
S.S. Nueva transformación de parámetro 'K' para mostrar matrices asociativas como pares clave-valor.
Está bien explicado en elManual de referencia de Bash → 3.5.3 Expansión de parámetros de Shell:
${parameter@operator}
k
Produce una versión posiblemente entrecomillada del valor del parámetro, excepto que imprime los valores de matrices indexadas y asociativas como una secuencia de pares clave-valor entrecomillados (consulte Matrices).
Respuesta4
Desdecomponerhace lo que quieres, ¿por qué no simplemente editar su salida?
typeset -p array | sed s/^.*\(// | tr -d ")\'\"" | tr "[" "\n" | sed s/]=/' = '/
da
a2 = 2
a1 = 1
b1 = bbb
Dónde
array='([a2]="2" [a1]="1" [b1]="bbb" )'
Detallado, pero es bastante fácil ver cómo funciona el formato: simplemente ejecute el proceso con progresivamente mássedytrcomandos. Modifíquelos para adaptarlos a sus bonitos gustos de impresión.