Creo que estás preguntando dos cosas diferentes allí.
¿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.
¿Hay una forma más limpia de obtener / imprimir solo la parte clave = valor de la salida?
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 tratar con a) la posibilidad de que las variables contengan paréntesis o corchetes, b) la cita quedeclare -p
debe agregar para hacer que su salida sea una entrada válida para el shell.
Por ejemplo, su expansión b="${a##*(}"
come algunos de los valores, si alguna clave / valor contiene un paréntesis de apertura. Esto se debe a que usó ##
, lo que elimina el prefijo más largo . Lo mismo para c="${b%% )*}"
. Aunque, por supuesto, podría igualar la plantilla impresa con declare
más precisión, aún le costaría mucho trabajo si no quisiera todas las citas.
Esto no se ve muy bien a menos que lo necesite.
$ 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 desee:
# 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 es simple. cambiar el formato de salida de lo contrario (quite los corchetes alrededor de la clave, coloque todos los pares clave / valor en una sola línea ...). Si necesita citar algo diferente al shell en sí, deberá hacerlo usted mismo, pero al menos tiene los datos sin procesar para trabajar. (Si tiene nuevas líneas en las claves o valores, probablemente necesitará algunas citas).
Con un Bash actual (4.4, creo), también podría usarlo en printf "[%s]=%s" "${x@Q}" "${array[$x]@Q}"
lugar de printf "%q=%q"
. Produce un formato citado algo más agradable, pero por supuesto es un poco más trabajo para recordar escribir. (Y cita el caso de la esquina @
como clave de matriz, que %q
no cita).
Si el bucle for parece demasiado cansado para escribir, guárdelo como una función en algún lugar (sin citar 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
printf ...%q...
variante no es adecuada para reiniciar al shell si la matriz tiene una@
clave como% q no la cita ya=([@]=value)
es un error de sintaxisbash
."${x@Q}"
cita eso también, ya que cita todas las cadenas (y se ve mejor). agregó una nota sobre el uso de eso.zsh
con sus banderas de expansión variable (que son anteriores a bash por décadas y con las cuales puede elegir el estilo de cotización: $ {(q) var}, $ {(qq) var} ...) para un mejor diseño. bash tiene el mismo problema que mksh en que no cita la cadena vacía (no es un problema aquí ya que de todos modos bash no admite claves vacías). Además, al usar estilos de comillas que no sean comillas simples (${var@Q}
recurre a$'...'
algunos valores) es importante que el código se reintroduzca en la misma configuración regional.x=; echo "${x@Q}"
da''
,unset x; echo "${x@Q}"
no da nada.) Bash@Q
parece preferir$'\n'
a una nueva línea literal, lo que en realidad puede ser bueno en algunas situaciones (pero no puedo decir lo que otros prefieren). Por supuesto, tener una opción allí no sería malo.$'...'
sintaxis es un problema potencial en cosas comoLC_ALL=zh_HK.big5hkscs bash -c 'a=$'\''\n\u3b1'\''; printf "%s\n" "${a@Q}"'
qué salidas$'\n<0xa3><0x5c>'
y0x5c
solo es una barra invertida, por lo que tendría un problema si esa cita se interpretara en una configuración regional diferente.2 tenedor
Tal vez esto:
3 tenedores
o esto:
Sin tenedor
para ser comparado con
Comparación de tiempos de ejecución
Como la última sintaxis no usa fork, podrían ser más rápidos:
Pero esta afirmación no se mantiene si la matriz se hace grande; Si la reducción de las horquillas es eficiente para procesos pequeños, el uso de herramientas dedicadas es más eficiente para procesos más grandes.
Observación
Como ambas soluciones ( bifurcadas ) usan alineación , ninguna de ellas funcionará si alguna variable contiene una nueva línea . En este caso, la única forma es un
for
bucle.fuente
for
. Lo que realmente es una lástima.pr
es más corto ... ¡No estoy seguro de que lapr
sintaxis se mantenga más lenta, incluso con matrices grandes!${!array[@]}
y${array[@]}
primero para que eso funcione.paste
es más largo que elfor
bucle en la pregunta escrita en una líneafor i in "${!array[@]}"; do echo "$i=${array[$i]}" ; done
, pero requiere dos subcapas y un programa externo. ¿Cómo es eso más ordenado? La soluciónpr
también se rompe si hay muchos elementos, ya que intenta paginar la salida. Tendría que usar algo como lo| pr -2t -l"${#array[@]}"
que está empezando a ser difícil de recordar en comparación con el bucle simple, y nuevamente, es más largo.bash
,cmd1 | cmd2
significa 2 tenedores, incluso si cmd1 o cmd2 o ambos están integrados.Si está buscando un shell con mejor soporte de matriz asociativa, intente
zsh
.En
zsh
(donde se agregaron matrices asociativas en 1998, en comparación con 1993 para ksh93 y 2009 para bash),$var
o se${(v)var}
expande a los valores (no vacíos) del hash,${(k)var}
a las teclas (no vacías) (en el mismo orden), y${(kv)var}
a las dos claves y valores.Para preservar los valores vacíos, como para las matrices, debe citar y usar la
@
bandera.Entonces, para imprimir las claves y los valores, es solo cuestión de
Aunque para tener en cuenta un hash posiblemente vacío, debe hacer:
También tenga en cuenta que zsh usa una sintaxis de definición de matriz mucho más sensata y útil que
ksh93
's (copiada porbash
):Lo que hace que sea mucho más fácil copiar o fusionar matrices asociativas:
(no puede copiar fácilmente un hash sin un bucle con
bash
, y tenga en cuenta quebash
actualmente no admite claves vacías o clave / valores con bytes NUL).Consulte también las
zsh
funciones de compresión de matrices que normalmente necesitará para trabajar con matrices asociativas:fuente
Dado que la composición tipográfica hace lo que desea, ¿por qué no simplemente editar su salida?
da
Dónde
Detallado, pero es bastante fácil ver cómo funciona el formateo: simplemente ejecute la tubería con progresivamente más comandos sed y tr . Modifíquelos para adaptarse a los gustos bonitos de impresión.
fuente
sed
sytr
s no es mucho más simple que unfor
bucle conprintf
.tr
traducir carácter por carácter, no coincide con las cadenas?tr "]=" " ="
cambia "]" a un espacio y un=
a un=
, independientemente de la posición. Así que probablemente podrías combinar los trestr
en uno.Una opción más es enumerar todas las variables y grep para la que desee.
set | grep -e '^aa='
Lo uso para depurar. Dudo que sea muy eficiente ya que enumera todas las variables.
Si hacía esto a menudo, podría hacerlo una función como esta:
aap() { set | grep -e "^$1="; }
Lamentablemente, cuando verificamos el rendimiento con el tiempo:
$ time aap aa aa=([0]="abc") . real 0m0.014s user 0m0.003s sys 0m0.006s
Por lo tanto, si hicieras esto muy a menudo, querrías la versión NO FORKS de @ F.Hauri porque es mucho más rápido.
fuente