Necesitábamos un script que simulara matrices asociativas o una estructura de datos similar a un mapa para Shell Scripting, ¿cualquier cuerpo?
bash
shell
hashtable
associative-array
Irfan Zulfiqar
fuente
fuente
Otra opción, si la portabilidad no es su principal preocupación, es utilizar matrices asociativas integradas en el shell. Esto debería funcionar en bash 4.0 (disponible ahora en la mayoría de las distribuciones principales, aunque no en OS X a menos que lo instale usted mismo), ksh y zsh:
Dependiendo del caparazón, es posible que deba hacer un en
typeset -A newmap
lugar dedeclare -A newmap
, o en algunos puede que no sea necesario en absoluto.fuente
test -z ${variable+x}
(x
no importa, podría ser cualquier cadena). Para una matriz asociativa en Bash, puede hacer algo similar; utilizartest -z ${map[key]+x}
.Otra forma de 4 que no es bash.
También puede lanzar una declaración if para buscar allí. si [[$ var = ~ / blah /]]. o lo que sea.
fuente
Creo que es necesario dar un paso atrás y pensar en lo que realmente es un mapa o matriz asociativa. Todo lo que es es una forma de almacenar un valor para una clave determinada y recuperar ese valor de forma rápida y eficiente. Es posible que también desee poder iterar sobre las claves para recuperar cada par de clave-valor, o eliminar claves y sus valores asociados.
Ahora, piense en una estructura de datos que utilice todo el tiempo en la creación de scripts de shell, e incluso solo en el shell sin escribir un script, que tenga estas propiedades. ¿Perplejo? Es el sistema de archivos.
Realmente, todo lo que necesita para tener una matriz asociativa en la programación de shell es un directorio temporal.
mktemp -d
es su constructor de matriz asociativa:Si no tiene ganas de usar
echo
ycat
, siempre puede escribir algunos pequeños envoltorios; estos están modelados a partir de Irfan, aunque solo generan el valor en lugar de establecer variables arbitrarias como$value
:editar : Este enfoque es en realidad bastante más rápido que la búsqueda lineal usando sed sugerido por el interrogador, así como más robusto (permite que las claves y valores contengan -, =, espacio, qnd ": SP:"). El hecho de que utilice el sistema de archivos no lo hace lento; en realidad, nunca se garantiza que estos archivos se escriban en el disco a menos que llame
sync
; para archivos temporales como este con una vida útil corta, no es improbable que muchos de ellos nunca se escriban en el disco.Hice algunos puntos de referencia del código de Irfan, la modificación de Jerry del código de Irfan y mi código, usando el siguiente programa controlador:
Los resultados:
fuente
Bash4 admite esto de forma nativa. No uses
grep
oeval
, son los trucos más feos.Para obtener una respuesta detallada y detallada con código de ejemplo, consulte: /programming/3467959
fuente
Ejemplo:
fuente
Ahora respondiendo a esta pregunta.
Los siguientes scripts simulan matrices asociativas en scripts de shell. Es simple y muy fácil de entender.
El mapa no es más que una cadena interminable que tiene keyValuePair guardado como --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company
los espacios se reemplazan con ': SP:' para los valores
editar: Acabo de agregar otro método para recuperar todas las claves.
fuente
eval
obteniendo datos como si fueran un código bash, y lo que es más: no los citas correctamente. Ambos causan una gran cantidad de errores e inyección de código arbitrario.Para Bash 3, hay un caso particular que tiene una solución agradable y simple:
Si no desea manejar muchas variables, o las claves son simplemente identificadores de variables no válidos, y se garantiza que su matriz tiene menos de 256 elementos , puede abusar de los valores de retorno de la función. Esta solución no requiere ninguna subcapa ya que el valor está disponible como una variable, ni ninguna iteración para que el rendimiento grite. También es muy legible, casi como la versión Bash 4.
Aquí está la versión más básica:
Recuerde, use comillas simples
case
, de lo contrario, está sujeto a globbing. Realmente útil para hashes estáticos / congelados desde el principio, pero se podría escribir un generador de índices a partir de unahash_keys=()
matriz.Tenga cuidado, el valor predeterminado es el primero, por lo que es posible que desee dejar de lado el elemento cero:
Advertencia: la longitud ahora es incorrecta.
Alternativamente, si desea mantener la indexación basada en cero, puede reservar otro valor de índice y protegerse contra una clave inexistente, pero es menos legible:
O, para mantener la longitud correcta, desplace el índice en uno:
fuente
Puede utilizar nombres de variables dinámicas y dejar que los nombres de las variables funcionen como las claves de un mapa hash.
Por ejemplo, si tiene un archivo de entrada con dos columnas, nombre, crédito, como el ejemplo a continuación, y desea sumar los ingresos de cada usuario:
El siguiente comando sumará todo, usando variables dinámicas como claves, en forma de mapa _ $ {persona} :
Para leer los resultados:
La salida será:
Desarrollando estas técnicas, estoy desarrollando en GitHub una función que funciona como un objeto HashMap , shell_map .
Para crear " instancias HashMap ", la función shell_map puede crear copias de sí misma con diferentes nombres. Cada nueva copia de función tendrá una variable $ FUNCNAME diferente. $ FUNCNAME luego se usa para crear un espacio de nombres para cada instancia de Map.
Las claves del mapa son variables globales, en la forma $ FUNCNAME_DATA_ $ KEY, donde $ KEY es la clave agregada al mapa. Estas variables son variables dinámicas .
A continuación, pondré una versión simplificada para que pueda usarla como ejemplo.
Uso:
fuente
Otra forma más que no es bash-4 (es decir, bash 3, compatible con Mac):
Huellas dactilares:
La función con
case
actúa como una matriz asociativa. Desafortunadamente, no se puede usarreturn
, por lo que tieneecho
su salida, pero esto no es un problema, a menos que sea un purista que evite bifurcar subcapas.fuente
Qué lástima que no vi la pregunta antes: escribí el marco de shell de la biblioteca que contiene, entre otros, los mapas (matrices asociativas). La última versión se puede encontrar aquí .
Ejemplo:
fuente
Añadiendo otra opción, si jq está disponible:
fuente
Descubrí que es cierto, como ya se mencionó, que el método de mejor rendimiento es escribir key / vals en un archivo y luego usar grep / awk para recuperarlos. Suena como todo tipo de E / S innecesarias, pero el caché de disco entra en acción y lo hace extremadamente eficiente, mucho más rápido que intentar almacenarlos en la memoria usando uno de los métodos anteriores (como muestran los puntos de referencia).
Aquí hay un método rápido y limpio que me gusta:
Si desea aplicar un valor único por clave, también puede hacer una pequeña acción grep / sed en hput ().
fuente
Hace varios años escribí una biblioteca de scripts para bash que admitía matrices asociativas entre otras características (registro, archivos de configuración, soporte extendido para argumentos de línea de comando, generar ayuda, pruebas unitarias, etc.). La biblioteca contiene un contenedor para matrices asociativas y cambia automáticamente al modelo apropiado (interno para bash4 y emulado para versiones anteriores). Se llamaba shell-framework y estaba alojado en origo.ethz.ch, pero hoy el recurso está cerrado. Si alguien aún lo necesita, puedo compartirlo contigo.
fuente
Shell no tiene un mapa integrado como estructura de datos, yo uso una cadena sin procesar para describir elementos como ese:
cuando se extraen elementos y sus atributos:
Esto no parece más inteligente que la respuesta de otras personas, pero es fácil de entender para que la gente nueva lo pueda burlar.
fuente
Modifiqué la solución de Vadim con lo siguiente:
El cambio es map_get para evitar que devuelva errores si solicita una clave que no existe, aunque el efecto secundario es que también ignorará silenciosamente los mapas faltantes, pero se adapta mejor a mi caso de uso ya que acabo de quería buscar una clave para omitir elementos en un bucle.
fuente
Respuesta tardía, pero considere abordar el problema de esta manera, utilizando la lectura incorporada de bash como se ilustra en el 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 utilizado el | delimitador porque los especificadores de rango de puertos pueden requerir dos puntos, es decir, 6001: 6010 .
fuente