Ассоциативные массивы в скриптах Shell - PullRequest
106 голосов
/ 27 марта 2009

Нам потребовался скрипт, имитирующий ассоциативные массивы или структуру, подобную Map, для сценариев оболочки, любое тело?

Ответы [ 17 ]

1 голос
/ 02 марта 2011

Как жаль, что я не видел вопроса раньше - я написал библиотеку shell-framework , которая содержит, помимо прочего, карты (ассоциативные массивы). Последнюю версию можно найти здесь .

Пример:

#!/bin/bash 
#include map library
shF_PATH_TO_LIB="/usr/lib/shell-framework"
source "${shF_PATH_TO_LIB}/map"

#simple example get/put
putMapValue "mapName" "mapKey1" "map Value 2"
echo "mapName[mapKey1]: $(getMapValue "mapName" "mapKey1")"

#redefine old value to new
putMapValue "mapName" "mapKey1" "map Value 1"
echo "after change mapName[mapKey1]: $(getMapValue "mapName" "mapKey1")"

#add two new pairs key/values and print all keys
putMapValue "mapName" "mapKey2" "map Value 2"
putMapValue "mapName" "mapKey3" "map Value 3"
echo -e "mapName keys are \n$(getMapKeys "mapName")"

#create new map
putMapValue "subMapName" "subMapKey1" "sub map Value 1"
putMapValue "subMapName" "subMapKey2" "sub map Value 2"

#and put it in mapName under key "mapKey4"
putMapValue "mapName" "mapKey4" "subMapName"

#check if under two key were placed maps
echo "is map mapName[mapKey3]? - $(if isMap "$(getMapValue "mapName" "mapKey3")" ; then echo Yes; else echo No; fi)"
echo "is map mapName[mapKey4]? - $(if isMap "$(getMapValue "mapName" "mapKey4")" ; then echo Yes; else echo No; fi)"

#print map with sub maps
printf "%s\n" "$(mapToString "mapName")"
0 голосов
/ 13 июля 2018

Добавление другой опции, если jq доступен:

export NAMES="{
  \"Mary\":\"100\",
  \"John\":\"200\",
  \"Mary\":\"50\",
  \"John\":\"300\",
  \"Paul\":\"100\",
  \"Paul\":\"400\",
  \"David\":\"100\"
}"
export NAME=David
echo $NAMES | jq --arg v "$NAME" '.[$v]' | tr -d '"' 
0 голосов
/ 09 февраля 2010

Как уже упоминалось, я считаю, что самый эффективный метод - записать ключ / vals в файл, а затем использовать grep / awk для их получения. Это звучит как все виды ненужного ввода-вывода, но дисковый кеш срабатывает и делает его чрезвычайно эффективным - гораздо быстрее, чем пытаться сохранить их в памяти одним из перечисленных выше способов (как показывают тесты).

Вот быстрый, чистый метод, который мне нравится:

hinit() {
    rm -f /tmp/hashmap.$1
}

hput() {
    echo "$2 $3" >> /tmp/hashmap.$1
}

hget() {
    grep "^$2 " /tmp/hashmap.$1 | awk '{ print $2 };'
}

hinit capitols
hput capitols France Paris
hput capitols Netherlands Amsterdam
hput capitols Spain Madrid

echo `hget capitols France` and `hget capitols Netherlands` and `hget capitols Spain`

Если вы хотите использовать одно значение для каждого ключа, вы также можете выполнить небольшое действие grep / sed в hput ().

0 голосов
/ 15 сентября 2015

Поздний ответ, но подумайте над решением этой проблемы, используя встроенную в bash read , как показано во фрагменте кода из сценария брандмауэра ufw, который следует ниже. Этот подход имеет то преимущество, что использует столько наборов полей с разделителями (не только 2), сколько необходимо. Мы использовали разделитель | , поскольку для спецификаторов диапазона портов может потребоваться двоеточие, то есть 6001: 6010 .

#!/usr/bin/env bash

readonly connections=(       
                            '192.168.1.4/24|tcp|22'
                            '192.168.1.4/24|tcp|53'
                            '192.168.1.4/24|tcp|80'
                            '192.168.1.4/24|tcp|139'
                            '192.168.1.4/24|tcp|443'
                            '192.168.1.4/24|tcp|445'
                            '192.168.1.4/24|tcp|631'
                            '192.168.1.4/24|tcp|5901'
                            '192.168.1.4/24|tcp|6566'
)

function set_connections(){
    local range proto port
    for fields in ${connections[@]}
    do
            IFS=$'|' read -r range proto port <<< "$fields"
            ufw allow from "$range" proto "$proto" to any port "$port"
    done
}

set_connections
0 голосов
/ 05 августа 2015

В оболочке нет встроенной карты, такой как структура данных, я использую необработанную строку для описания таких элементов:

ARRAY=(
    "item_A|attr1|attr2|attr3"
    "item_B|attr1|attr2|attr3"
    "..."
)

при извлечении предметов и их атрибутов:

for item in "${ARRAY[@]}"
do
    item_name=$(echo "${item}"|awk -F "|" '{print $1}')
    item_attr1=$(echo "${item}"|awk -F "|" '{print $2}')
    item_attr2=$(echo "${item}"|awk -F "|" '{print $3}')

    echo "${item_name}"
    echo "${item_attr1}"
    echo "${item_attr2}"
done

Это кажется не умным, чем ответ других людей, но легко понятным для новых людей.

0 голосов
/ 11 апреля 2013

Я изменил решение Вадима со следующим:

####################################################################
# Bash v3 does not support associative arrays
# and we cannot use ksh since all generic scripts are on bash
# Usage: map_put map_name key value
#
function map_put
{
    alias "${1}$2"="$3"
}

# map_get map_name key
# @return value
#
function map_get {
    if type -p "${1}$2"
        then
            alias "${1}$2" | awk -F "'" '{ print $2; }';
    fi
}

# map_keys map_name 
# @return map keys
#
function map_keys
{
    alias -p | grep $1 | cut -d'=' -f1 | awk -F"$1" '{print $2; }'
}

Изменение в map_get для предотвращения возврата ошибок, если вы запрашиваете ключ, который не существует, хотя побочным эффектом является то, что он также будет игнорировать пропущенные карты, но он лучше подходит для моего варианта использования поскольку я просто хотел проверить ключ, чтобы пропустить элементы в цикле.

0 голосов
/ 13 июня 2013

Несколько лет назад я написал библиотеку сценариев для bash, которая поддерживала ассоциативные массивы среди других функций (ведение журнала, файлы конфигурации, расширенная поддержка аргументов командной строки, генерация справки, модульное тестирование и т. Д.). Библиотека содержит оболочку для ассоциативных массивов и автоматически переключается на соответствующую модель (внутреннюю для bash4 и эмулирует для предыдущих версий). Он назывался shell-framework и размещался на сайте origo.ethz.ch, но сегодня ресурс закрыт. Если кому-то все еще это нужно, я могу поделиться с вами.

...