¿Cuál es el equivalente de los diccionarios de Python pero en Bash (debería funcionar en OS X y Linux).
bash
dictionary
hashtable
associative-array
Sridhar Ratnakumar
fuente
fuente
Respuestas:
Golpe 4
Bash 4 admite de forma nativa esta función. Asegúrate de que el hashbang de tu script sea más
#!/usr/bin/env bash
o menos#!/bin/bash
así que no termines usandosh
. Asegúrese de que está ejecutando su script directamente o ejecutescript
conbash script
. (En realidad no la ejecución de un script Bash con Bash no suceda, y será muy confuso!)Declaras una matriz asociativa haciendo:
Puede llenarlo con elementos utilizando el operador de asignación de matriz normal. Por ejemplo, si desea tener un mapa de
animal[sound(key)] = animal(value)
:O fusionarlos:
Luego úselos como matrices normales. Utilizar
animals['key']='value'
establecer valor"${animals[@]}"
para expandir los valores"${!animals[@]}"
(observe el!
) para expandir las teclasNo olvides citarlos:
Golpe 3
Antes de bash 4, no tienes matrices asociativas. No lo use
eval
para emularlos . Evitaeval
como la plaga, porque es la plaga de las secuencias de comandos de shell. La razón más importante es queeval
trata sus datos como código ejecutable (también hay muchas otras razones).Primero y principal : considere actualizar a bash 4. Esto hará que todo el proceso sea mucho más fácil para usted.
Si hay una razón por la que no puede actualizar,
declare
es una opción mucho más segura. No evalúa los datos como el código bash como loeval
hace, y como tal no permite la inyección de código arbitrario con tanta facilidad.Preparemos la respuesta introduciendo los conceptos:
Primero, indirección.
En segundo lugar
declare
:Reunirlos:
Vamos a usarlo:
Nota:
declare
no se puede poner en una función. Cualquier uso dedeclare
dentro de una función bash convierte la variable que crea local en el alcance de esa función, lo que significa que no podemos acceder o modificar matrices globales con ella. (En bash 4 puede usar declare -g para declarar variables globales, pero en bash 4, puede usar matrices asociativas en primer lugar, evitando esta solución).Resumen:
declare -A
para matrices asociativas.declare
opción si no puede actualizar.awk
lugar y evite el problema por completo.fuente
4.x
y noy
.sudo port install bash
, para aquellos (sabiamente, en mi humilde opinión) que no están dispuestos a crear directorios en la RUTA para que todos los usuarios puedan escribir sin una escalada explícita de privilegios por proceso.Hay sustitución de parámetros, aunque también puede ser no PC ... como indirección.
La forma de BASH 4 es mejor, por supuesto, pero si necesita un hack ... solo un hack lo hará. Puede buscar en la matriz / hash con técnicas similares.
fuente
VALUE=${animal#*:}
proteger el caso dondeARRAY[$x]="caesar:come:see:conquer"
for animal in "${ARRAY[@]}"; do
Esto es lo que estaba buscando aquí:
Esto no funcionó para mí con bash 4.1.5:
fuente
Puede modificar aún más la interfaz hput () / hget () para que haya nombrado hashes de la siguiente manera:
y entonces
Esto le permite definir otros mapas que no entren en conflicto (p. Ej., 'Rcapitals' que realiza búsquedas de país por ciudad capital). Pero, de cualquier manera, creo que encontrarás que todo esto es bastante terrible, en cuanto al rendimiento.
Si realmente quieres una búsqueda rápida de hash, hay un truco terrible que funciona realmente bien. Es esto: escriba su clave / valores en un archivo temporal, uno por línea, luego use 'grep "^ $ key"' para sacarlos, utilizando tuberías con corte o awk o sed o lo que sea para recuperar los valores.
Como dije, suena terrible, y parece que debería ser lento y hacer todo tipo de IO innecesarias, pero en la práctica es muy rápido (el caché del disco es increíble, ¿no?), Incluso para hash muy grandes mesas. Tienes que imponer la unicidad de la clave tú mismo, etc. Incluso si solo tienes unos cientos de entradas, el combo de archivo de salida / grep será bastante más rápido, en mi experiencia varias veces más rápido. También come menos memoria.
Aquí hay una forma de hacerlo:
fuente
Solo usa el sistema de archivos
El sistema de archivos es una estructura de árbol que se puede usar como un mapa hash. Su tabla hash será un directorio temporal, sus claves serán nombres de archivos y sus valores serán contenidos de archivos. La ventaja es que puede manejar enormes hashmaps y no requiere un shell específico.
Creación de tabla hash
hashtable=$(mktemp -d)
Agregar un elemento
echo $value > $hashtable/$key
Leer un elemento
value=$(< $hashtable/$key)
Actuación
Por supuesto, es lento, pero no tan lento. Lo probé en mi máquina, con un SSD y btrfs , y hace alrededor de 3000 elementos de lectura / escritura por segundo .
fuente
mkdir -d
? (No 4.3, en Ubuntu 14. Recurriríamkdir /run/shm/foo
, o si eso llenara RAMmkdir /tmp/foo
mktemp -d
se entiende en su lugar?$value=$(< $hashtable/$key)
yvalue=$(< $hashtable/$key)
? ¡Gracias!fuente
${var#start}
elimina el texto que comienza desde el principio del valor almacenado en la variable var .Considere una solución usando el bash builtin read como se ilustra dentro del fragmento de código de un script de firewall ufw que sigue. Este enfoque tiene la ventaja de utilizar tantos conjuntos de campos delimitados (no solo 2) como se desee. Hemos usado el | delimitador porque los especificadores de rango de puertos pueden requerir dos puntos, es decir, 6001: 6010 .
fuente
IFS=$'|' read -r first rest <<< "$fields"
Estoy de acuerdo con @lhunath y otros en que la matriz asociativa es el camino a seguir con Bash 4. Si está atascado en Bash 3 (OSX, distribuciones antiguas que no puede actualizar), puede usar también expr, que debería estar en todas partes, una cadena y expresiones regulares. Me gusta especialmente cuando el diccionario no es demasiado grande.
Escriba su mapa como una cadena (tenga en cuenta el separador ',' también al principio y al final)
Use una expresión regular para extraer los valores
Dividir la cadena para enumerar los elementos.
Ahora puedes usarlo:
fuente
Realmente me gustó la respuesta de Al P, pero quería que la unicidad se aplicara de manera barata, así que lo llevé un paso más allá: use un directorio. Hay algunas limitaciones obvias (límites de archivos de directorio, nombres de archivo no válidos) pero debería funcionar para la mayoría de los casos.
También funciona un poco mejor en mis pruebas.
Solo pensé en lanzarme. ¡Salud!
Editar: Agregar hdestroy ()
fuente
Dos cosas, puedes usar memoria en lugar de / tmp en cualquier kernel 2.6 usando / dev / shm (Redhat) otras distribuciones pueden variar. También se puede volver a implementar hget usando read como sigue:
Además, suponiendo que todas las claves son únicas, el retorno cortocircuita el ciclo de lectura y evita tener que leer todas las entradas. Si su implementación puede tener claves duplicadas, simplemente omita el retorno. Esto ahorra el gasto de leer y bifurcar grep y awk. El uso de / dev / shm para ambas implementaciones arrojó lo siguiente usando time hget en un hash de 3 entradas buscando la última entrada:
Grep / Awk:
Leer / eco:
en invocaciones múltiples nunca vi menos de una mejora del 50%. Todo esto se puede atribuir al tenedor sobre la cabeza, debido al uso de
/dev/shm
.fuente
Un compañero de trabajo acaba de mencionar este hilo. Implementé independientemente tablas hash dentro de bash, y no depende de la versión 4. De una publicación mía en mi blog en marzo de 2010 (antes de algunas de las respuestas aquí ...) titulada Tablas hash en bash :
Me previamente acostumbrado
cksum
a picadillo pero desde entonces han traducido hashCode cadena de Java a los nativos bash / zsh.No es bidireccional, y la forma incorporada es mucho mejor, pero tampoco debería usarse. Bash es para casos únicos rápidos, y tales cosas rara vez deben involucrar complejidad que pueda requerir hashes, excepto tal vez en usted
~/.bashrc
y sus amigos.fuente
Antes de bash 4, no hay una buena manera de usar matrices asociativas en bash. Su mejor opción es utilizar un lenguaje interpretado que realmente tenga soporte para tales cosas, como awk. Por otra parte, golpe del 4 hace apoyarlos.
En cuanto a las formas menos buenas en bash 3, aquí hay una referencia que podría ayudar: http://mywiki.wooledge.org/BashFAQ/006
fuente
Solución Bash 3:
Al leer algunas de las respuestas, reuní una pequeña función rápida que me gustaría contribuir para ayudar a otros.
fuente
También usé la forma bash4 pero encuentro un error molesto.
Necesitaba actualizar dinámicamente el contenido de la matriz asociativa, así que lo utilicé de esta manera:
Descubrí que con bash 4.3.11 agregar a una clave existente en el dict resultó en agregar el valor si ya está presente. Entonces, por ejemplo, después de alguna repetición, el contenido del valor era "checkKOcheckKOallCheckOK" y esto no era bueno.
No hay problema con bash 4.3.39 donde agregar una clave existente significa subestimar el valor real si ya está presente.
Resolví esto simplemente limpiando / declarando la matriz asociativa statusCheck antes del ciclo:
fuente
Creo HashMaps en bash 3 usando variables dinámicas. Le expliqué cómo funciona eso en mi respuesta a: Matrices asociativas en scripts de Shell
También puede echar un vistazo en shell_map , que es una implementación de HashMap hecha en bash 3.
fuente