2 tenedor

2 tenedor

¿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 forbucle. 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 -pdeben 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 declaremayor 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 forbucle, 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 %qno 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 forbucle.

Respuesta más sólida y detallada en StackOverflow

Respuesta3

Bash 5.1 permite una forma muy sencilla de mostrar matrices asociativas utilizando el Kvalor 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.

información relacionada