Matrices asociativas en scripts de shell

11

Vi un truco para implementar matrices asociativas en un script de shell. Por ejemplo, print array["apples"]podría escribirse como echo \$array$keywhere key = apples.

Sin embargo, no se mencionó cómo generar las claves para iterar sobre la matriz. La única forma en que podía pensar era almacenar las claves en una variable delimitada por espacios para poder usar un bucle for para iterar sobre la matriz.

Entonces, ¿hay alguna otra forma de almacenar las claves para su uso posterior?

Intelectual
fuente
55
Si está intentando utilizar matrices asociativas en un script de shell, es posible que su proyecto sea demasiado complejo para un script de shell :)
Martin von Wittich
@MartinvonWittich ¿por qué? Tengo un script de shell que ejecuta un script SQL en uno de los 3 posibles esquemas de base de datos. El esquema requerido se incluye en el nombre del archivo con una abreviatura. Necesito un mapeo entre esta abreviatura y el nombre del esquema real. ¿Qué mejor manera que una matriz asociativa, teniendo en cuenta que los nombres de esquema reales (no la abreviatura) pueden diferir entre entornos, por lo que una variable de matriz (cuyos valores se pueden establecer solo una vez) es perfecta
Slav
2
@Slav No estoy discutiendo contra las matrices asociativas, solo contra los scripts de shell donde se necesita tanta complejidad. Pero esa es solo mi preferencia personal; A menudo me encuentro empezando a escribir un script de shell y luego lo reescribo inmediatamente en Perl cuando me doy cuenta de que estoy excediendo un cierto umbral de complejidad.
Martin von Wittich

Respuestas:

20

Conchas con matrices asociativas

Algunos shells modernos proporcionan matrices asociativas: ksh93, bash ≥4, zsh. En ksh93 y bash, si aes una matriz asociativa, entonces "${!a[@]}"es la matriz de sus claves:

for k in "${!a[@]}"; do
  echo "$k -> ${a[$k]}"
done

En zsh, esa sintaxis solo funciona en el modo de emulación ksh. De lo contrario, debe usar la sintaxis nativa de zsh:

for k in "${(@k)a}"; do
  echo "$k -> $a[$k]"
done

${(k)a}También funciona si ano tiene una clave vacía.

En zsh, también puede hacer un bucle en keys y values ​​al mismo tiempo:

for k v ("${(@kv)a}") echo "$k -> $v"

Conchas sin matrices asociativas

Emular matrices asociativas en shells que no las tienen es mucho más trabajo. Si necesita matrices asociativas, probablemente sea hora de traer una herramienta más grande, como ksh93 o Perl.

Si necesita matrices asociativas en un simple shell POSIX, aquí hay una manera de simularlas, cuando las claves están restringidas para contener solo los caracteres 0-9A-Z_a-z(dígitos ASCII, letras y guiones bajos). Bajo este supuesto, las claves pueden usarse como parte de nombres de variables. Las funciones a continuación actúan sobre una matriz identificada por un prefijo de denominación, la "raíz", que no debe contener dos guiones bajos consecutivos.

## ainit STEM
## Declare an empty associative array named STEM.
ainit () {
  eval "__aa__${1}=' '"
}
## akeys STEM
## List the keys in the associatve array named STEM.
akeys () {
  eval "echo \"\$__aa__${1}\""
}
## aget STEM KEY VAR
## Set VAR to the value of KEY in the associative array named STEM.
## If KEY is not present, unset VAR.
aget () {
  eval "unset $3
        case \$__aa__${1} in
          *\" $2 \"*) $3=\$__aa__${1}__$2;;
        esac"
}
## aset STEM KEY VALUE
## Set KEY to VALUE in the associative array named STEM.
aset () {
  eval "__aa__${1}__${2}=\$3
        case \$__aa__${1} in
          *\" $2 \"*) :;;
          *) __aa__${1}=\"\${__aa__${1}}$2 \";;
        esac"
}
## aunset STEM KEY
## Remove KEY from the associative array named STEM.
aunset () {
  eval "unset __aa__${1}__${2}
        case \$__aa__${1} in
          *\" $2 \"*) __aa__${1}=\"\${__aa__${1}%%* $2 } \${__aa__${1}#* $2 }\";;
        esac"
}

(Advertencia, código no probado. No se proporciona detección de errores para claves y tallos sintácticamente inválidos).

Gilles 'SO- deja de ser malvado'
fuente
5

No estoy seguro de lo que quiere decir con tienda, pero puede iterar sobre las claves usando la ${!array[@]}sintaxis:

$ typeset -A foo=([key1]=bar [key2]=baz);
$ echo "${!foo[@]}" 
key2 key1

Entonces, para iterar:

$ for key in "${!foo[@]}"; do echo "$key : ${foo[$key]}"; done
key2 : baz
key1 : bar

Encontré un bonito y breve tutorial sobre esto aquí .


Como se señala en los comentarios a continuación, se agregaron matrices asociativas en la bashversión 4. Vea aquí un artículo de la revista Linux sobre el tema.

terdon
fuente
1
(bash version 4 only)Eso es algo importante a tener en cuenta. Tradicionalmente, las bashmatrices son solo numéricas.
Ricky Beam
1
Es posible que desee utilizar en typesetlugar de declareen sus ejemplos. Eso los haría portátiles entre bash 4 y ksh93, que primero implementó matrices asociativas de shell.
jlliagre
0

Conchas sin matrices asociativas

No es tan difícil cuando las teclas están restringidas a [0-9A-Za-z_](números, letras, guiones bajos).

El truco es en lugar de almacenar en la matriz [ $ clave ], almacenar en las variables matriz_ $ clave .

Conjunto:

eval "array_$key='$value'"

Obtener:

value=`eval echo '$'array_$key`

Nota: Los valores no pueden contener '(comillas simples).

Marián Černý
fuente
-1

esto funciona en bash

cert="first"
web="second"
declare -A assoc_array=(["cert"]="${cert}" ["web"]="${web}")
echo "first is" ${assoc_array[cert]}
echo "second is" ${assoc_array[web]}

O

#loop
for i in "${assoc_array[@]}"
do
   echo "$i"
done

No es necesario usar eval afaik

JamesD
fuente
1
Creo que te has perdido el punto de la pregunta.
G-Man dice 'Restablece a Monica' el