शैल लिपियों में साहचर्य सरणियाँ


116

हमें एक स्क्रिप्ट की आवश्यकता है जो शेल स्क्रिप्टिंग, किसी भी निकाय के लिए डेटा संरचना की तरह साहचर्य सरणियों या मानचित्र का अनुकरण करती है?

जवाबों:


20

इरफान के जवाब में जोड़ने के लिए , यहाँ एक छोटा और तेज़ संस्करण है get()क्योंकि इसके लिए मानचित्र सामग्री पर कोई पुनरावृत्ति की आवश्यकता नहीं है:

get() {
    mapName=$1; key=$2

    map=${!mapName}
    value="$(echo $map |sed -e "s/.*--${key}=\([^ ]*\).*/\1/" -e 's/:SP:/ /g' )"
}

16
एक उपधारा और पालना बनाना शायद ही इष्टतम है। Bash4 इसका मूल रूप से समर्थन करता है और bash3 में बेहतर विकल्प हैं।
लहनाथ

149

एक अन्य विकल्प, यदि पोर्टेबिलिटी आपकी मुख्य चिंता नहीं है, तो शेल के लिए बनाई गई साहचर्य सरणियों का उपयोग करना है। यह bash 4.0 में काम करना चाहिए (अब सबसे बड़े डिस्ट्रो पर उपलब्ध है, हालाँकि OS X पर नहीं जब तक कि आप इसे स्वयं इंस्टॉल न करें), ksh, और zsh:

declare -A newmap
newmap[name]="Irfan Zulfiqar"
newmap[designation]=SSE
newmap[company]="My Own Company"

echo ${newmap[company]}
echo ${newmap[name]}

शेल के आधार पर, आपको typeset -A newmapइसके बजाय करने की आवश्यकता हो सकती है declare -A newmap, या कुछ में यह बिल्कुल भी आवश्यक नहीं हो सकता है।


उत्तर पोस्ट करने के लिए धन्यवाद, मुझे लगता है कि यह उन लोगों के लिए सबसे अच्छा तरीका होगा जो बैश 4.0 या इसके बाद के संस्करण का उपयोग करेंगे।
इरफान जुल्फिकार

मुझे लगता है कि BASH_VERSION सेट है, और> = 4. 4. सुनिश्चित करने के लिए थोड़ा सा कीचड़ जोड़ूंगा, और हाँ, BASH 4 वास्तव में बहुत अच्छा है!
टिम पोस्ट

मैं कुछ इस तरह का उपयोग कर रहा हूँ। सरणी इंडेक्स / सबस्क्रिप्ट मौजूद नहीं होने पर त्रुटि को "पकड़ने" का सबसे अच्छा तरीका क्या है? उदाहरण के लिए, क्या होगा अगर मैं सबस्क्रिप्ट को कमांड लाइन विकल्प के रूप में ले रहा था, और उपयोगकर्ता ने एक टाइपो बनाया और "डिजाइन अनुपात" में प्रवेश किया? मुझे "खराब सरणी सबस्क्रिप्ट" त्रुटि मिलती है, लेकिन सरणी लुकअप के समय इनपुट को कैसे मान्य किया जाए, यह संभव नहीं है?
जेर

3
@Jer यह बहुत अस्पष्ट है, लेकिन यह निर्धारित करने के लिए कि यदि शेल में एक चर सेट है, तो आप उपयोग कर सकते हैं test -z ${variable+x}( xकोई फर्क नहीं पड़ता, कि कोई भी स्ट्रिंग हो सकती है)। बैश में एक साहचर्य सरणी के लिए, आप समान कर सकते हैं; उपयोग करें test -z ${map[key]+x}
ब्रायन कैंपबेल

95

एक और नॉन-बैश 4 तरीका।

#!/bin/bash

# A pretend Python dictionary with bash 3 
ARRAY=( "cow:moo"
        "dinosaur:roar"
        "bird:chirp"
        "bash:rock" )

for animal in "${ARRAY[@]}" ; do
    KEY=${animal%%:*}
    VALUE=${animal#*:}
    printf "%s likes to %s.\n" "$KEY" "$VALUE"
done

echo -e "${ARRAY[1]%%:*} is an extinct animal which likes to ${ARRAY[1]#*:}\n"

तुम वहाँ के रूप में अच्छी तरह से खोज के लिए एक अगर बयान फेंक सकता है। अगर [[$ var = ~ / blah /]]। जो कुछ भी।


2
यह तरीका अच्छा है जब आपके पास वास्तव में बैश 4 नहीं है। लेकिन मुझे लगता है कि VALUE को लाने वाली रेखा इस तरह सुरक्षित होगी: VALUE = $ {पशु # *:}। केवल एक # चरित्र के साथ, मिलान पहले ":" पर रोक देगा। यह मानों को ":" को भी सम्‍मिलित करता है।
बजे सीड-ले-पिंगोइन

@ Ced-le-pingouin ~ यह एक महान बिंदु है! मैंने उसे नहीं पकड़ा। आपके सुझाए गए सुधारों को दर्शाने के लिए मैंने अपनी पोस्ट संपादित की है।
बुब्नॉफ़

1
यह BASH पैरामीटर प्रतिस्थापन का उपयोग करते हुए साहचर्य सरणियों का एक सुंदर हैकिंग अनुकरण है। "की" परम उप सब कुछ बृहदान्त्र से पहले सब कुछ और मूल्य पैटर्न बृहदान्त्र के बाद सब कुछ स्थानापन्न । रेगेक्स वाइल्डकार्ड मैच के समान। तो नहीं एक सच्चे साहचर्य सरणी। अनुशंसित नहीं है जब तक आपको BASH 3 या उससे नीचे हैश / साहचर्य सरणी-जैसी कार्यक्षमता को समझने के लिए आसान तरीके की आवश्यकता न हो। हालांकि यह काम करता है! यहाँ और अधिक: tldp.org/LDP/abs/html/parameter-substitution.html#PSOREX2
बुबनाफ

1
यह एक साहचर्य सरणी को लागू नहीं करता है क्योंकि यह कुंजी द्वारा किसी आइटम को देखने का एक तरीका प्रदान नहीं करता है। यह केवल संख्यात्मक सूचकांक से प्रत्येक कुंजी (और मूल्य) को खोजने का एक तरीका प्रदान करता है। (सरणी के माध्यम से पुनरावृत्ति करके एक आइटम कुंजी द्वारा पाया जा सकता है, लेकिन यह वह नहीं है जो एक साहचर्य सरणी के लिए वांछित है।)
एरिक पोस्टपिसिल

@EricPostpischil सच। यह केवल एक हैक है। यह एक व्यक्ति को सेटअप में परिचित वाक्यविन्यास का उपयोग करने की अनुमति देता है लेकिन फिर भी आपको सरणी के माध्यम से पुनरावृति की आवश्यकता होती है जैसा कि आप कहते हैं। मैंने अपनी पिछली टिप्पणी में स्पष्ट होने की कोशिश की है कि यह निश्चित रूप से एक सहयोगी सरणी नहीं है और अगर आपके पास विकल्प हैं तो मैं इसकी सिफारिश भी नहीं करता। मेरे विचार में इसके पक्ष में एकमात्र बिंदु यह है कि पाइथन जैसी अन्य भाषाओं से परिचित लोगों के लिए लिखना और उपयोग करना आसान है। यदि आप एक ऐसे बिंदु पर हैं जहां आप वास्तव में BASH 3 में साहचर्य सरणियों को लागू करना चाहते हैं तो आपको अपने कदमों को थोड़ा पीछे करना पड़ सकता है।
बुबनाफ

34

मुझे लगता है कि आपको एक कदम और सोचने की ज़रूरत है कि वास्तव में नक्शा, या साहचर्य सरणी क्या है। यह सब किसी दिए गए कुंजी के लिए एक मूल्य को संग्रहीत करने का एक तरीका है, और उस मूल्य को जल्दी और कुशलता से वापस प्राप्त करता है। आप हर कुंजी मान जोड़ी को पुनः प्राप्त करने, या कुंजियों और उनके संबंधित मूल्यों को हटाने के लिए कुंजियों पर पुनरावृति करने में सक्षम होना चाह सकते हैं।

अब, एक डेटा संरचना के बारे में सोचें जो आप शेल स्क्रिप्टिंग में हर समय उपयोग करते हैं, और यहां तक ​​कि शेल में सिर्फ एक स्क्रिप्ट लिखे बिना, जिसमें ये गुण हैं। स्टम्प्ड? यह फाइलसिस्टम है।

वास्तव में, आपको शेल प्रोग्रामिंग में एक सहयोगी सरणी होना चाहिए, यह एक अस्थायी निर्देशिका है। mktemp -dआपका सहयोगी सरणी निर्माता है:

prefix=$(basename -- "$0")
map=$(mktemp -dt ${prefix})
echo >${map}/key somevalue
value=$(cat ${map}/key)

यदि आप का उपयोग करने का मन नहीं है echoऔर cat, आप हमेशा कुछ छोटे रैपर लिख सकते हैं; इन लोगों को इरफ़ान के मॉडल से अलग किया जाता है, हालांकि वे मनमाने ढंग से चर सेट करने के बजाय केवल मूल्य का उत्पादन करते हैं $value:

#!/bin/sh

prefix=$(basename -- "$0")
mapdir=$(mktemp -dt ${prefix})
trap 'rm -r ${mapdir}' EXIT

put() {
  [ "$#" != 3 ] && exit 1
  mapname=$1; key=$2; value=$3
  [ -d "${mapdir}/${mapname}" ] || mkdir "${mapdir}/${mapname}"
  echo $value >"${mapdir}/${mapname}/${key}"
}

get() {
  [ "$#" != 2 ] && exit 1
  mapname=$1; key=$2
  cat "${mapdir}/${mapname}/${key}"
}

put "newMap" "name" "Irfan Zulfiqar"
put "newMap" "designation" "SSE"
put "newMap" "company" "My Own Company"

value=$(get "newMap" "company")
echo $value

value=$(get "newMap" "name")
echo $value

संपादित करें : यह दृष्टिकोण वास्तव में प्रश्नकर्ता द्वारा सुझाए गए सेड के उपयोग से रैखिक खोज की तुलना में काफी तेज है, साथ ही साथ और अधिक मजबूत है (यह कुंजी और मूल्यों को शामिल करने की अनुमति देता है -, =, अंतरिक्ष, qnd ": SP:")। तथ्य यह है कि यह फाइल सिस्टम का उपयोग करता है, इसे धीमा नहीं करता है; जब तक आप कॉल नहीं करते ये डिस्क वास्तव में डिस्क पर लिखे जाने की गारंटी नहीं है sync; एक छोटे जीवनकाल के साथ इस तरह की अस्थायी फ़ाइलों के लिए, यह संभावना नहीं है कि उनमें से कई डिस्क पर कभी नहीं लिखे जाएंगे।

मैंने इरफ़ान के कोड के कुछ बेंचमार्क किए, जेरी ने इरफ़ान के कोड का संशोधन किया, और मेरे कोड ने निम्नलिखित ड्राइवर प्रोग्राम का उपयोग किया:

#!/bin/sh

mapimpl=$1
numkeys=$2
numvals=$3

. ./${mapimpl}.sh    #/ <- fix broken stack overflow syntax highlighting

for (( i = 0 ; $i < $numkeys ; i += 1 ))
do
    for (( j = 0 ; $j < $numvals ; j += 1 ))
    do
        put "newMap" "key$i" "value$j"
        get "newMap" "key$i"
    done
done

परिणाम:

    $ समय ।/driver.sh इरफान 10 5

    वास्तविक 0m0.975s
    उपयोगकर्ता 0m0.280s
    sys 0m0.691s

    $ समय ।/driver.sh ब्रायन 10 5

    असली 0m0.226s
    उपयोगकर्ता 0m0.057s
    sys 0m0.123s

    $ समय ।/driver.sh जेरी 10 5

    असली 0m0.706s
    उपयोगकर्ता 0m0.228s
    sys 0m0.530s

    $ समय ।/driver.sh इरफान 100 5

    असली 0m10.633s
    उपयोगकर्ता 0m4.366s
    sys 0m7.127s

    $ समय ।/driver.sh ब्रायन 100 5

    असली 0m1.682s
    उपयोगकर्ता 0m0.546s
    sys 0m1.082s

    $ समय ।/driver.sh जेरी 100 5

    असली 0m9.315s
    उपयोगकर्ता 0m4.565s
    sys 0m5.446s

    $ समय ।/driver.sh इरफान 10 500

    असली 1m46.197s
    उपयोगकर्ता 0m44.869s
    sys 1m12.282s

    $ समय ।/driver.sh ब्रायन 10 500

    वास्तविक 0m16.003s
    उपयोगकर्ता 0m5.135s
    sys 0m10.396s

    $ समय ।/driver.sh जेरी 10 500

    वास्तविक 1m24.414s
    उपयोगकर्ता 0m39.696s
    sys 0m54.834s

    $ समय ।/driver.sh इरफान 1000 5

    असली 4m25.145s
    उपयोगकर्ता 3m17.286s
    sys 1m21.490s

    $ समय ।/driver.sh ब्रायन 1000 5

    असली 0m19.442s
    उपयोगकर्ता 0m5.287s
    sys 0m10.751s

    $ समय ।/driver.sh जेरी 1000 5

    असली 5m29.136s
    उपयोगकर्ता 4m48.926s
    sys 0m59.336s


1
मुझे नहीं लगता कि आपको नक्शे के लिए फ़ाइल सिस्टम का उपयोग करना चाहिए, जो मूल रूप से कुछ के लिए IO का उपयोग कर रहा है जिसे आप मेमोरी में काफी तेजी से कर सकते हैं।
इरफान जुल्फिकार

9
जरूरी नहीं कि फाइलें कभी डिस्क पर लिखी जाएं; जब तक आप सिंक नहीं कहते हैं, ऑपरेटिंग सिस्टम बस उन्हें स्मृति में छोड़ सकता है। आपका कोड sed को कॉल कर रहा है और कई रैखिक खोज कर रहा है, जो सभी बहुत धीमी हैं। मैंने कुछ त्वरित बेंचमार्क किए, और मेरा संस्करण 5-35 गुना तेज है।
ब्रायन कैंपबेल

दूसरी ओर, bash4 के मूल सरणियाँ काफी बेहतर दृष्टिकोण हैं और bash3 में आप अभी भी डिक्लेयर और इनडायरेक्शन के उपयोग के बिना डिस्क से सब कुछ बंद रख सकते हैं।
लहनाथ

7
"तेज" और "शेल" वास्तव में वैसे भी एक साथ नहीं चलते हैं: निश्चित रूप से उस गति के मुद्दों के लिए नहीं हैं जो हम "मिनीस्कुल आईओ से बचें" स्तर पर बात कर रहे हैं। आप किसी IO की गारंटी के लिए खोज और उपयोग / dev / shm कर सकते हैं।
16

2
इस समाधान ने मुझे आश्चर्यचकित कर दिया, और बस कमाल है। 2016 में अभी भी सही है। यह वास्तव में स्वीकृत उत्तर होना चाहिए।
गॉर्डन

7

Bash4 इसे मूल रूप से समर्थन करता है। उपयोग न करें grepया eval, वे हैक के सबसे बदसूरत हैं।

एक क्रिया के लिए, उदाहरण कोड के साथ विस्तृत जवाब देखें: /programming/3467959


7
####################################################################
# 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
{
    alias "${1}$2" | awk -F"'" '{ print $2; }'
}

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

उदाहरण:

mapName=$(basename $0)_map_
map_put $mapName "name" "Irfan Zulfiqar"
map_put $mapName "designation" "SSE"

for key in $(map_keys $mapName)
do
    echo "$key = $(map_get $mapName $key)
done

4

अब इस सवाल का जवाब दे रहे हैं।

निम्नलिखित स्क्रिप्ट शेल स्क्रिप्ट में साहचर्य सरणियों का अनुकरण करती है। इसका सरल और समझने में बहुत आसान है।

मानचित्र कुछ भी नहीं है, लेकिन कभी खत्म होने वाली स्ट्रिंग नहीं है, जिसमें keyValuePair को सहेजा गया है --name = Irfan --designation = SSE --company = My: SP: खुद: SP: कंपनी

मानों के लिए रिक्त स्थान ': SP:' से बदल दिए गए हैं

put() {
    if [ "$#" != 3 ]; then exit 1; fi
    mapName=$1; key=$2; value=`echo $3 | sed -e "s/ /:SP:/g"`
    eval map="\"\$$mapName\""
    map="`echo "$map" | sed -e "s/--$key=[^ ]*//g"` --$key=$value"
    eval $mapName="\"$map\""
}

get() {
    mapName=$1; key=$2; valueFound="false"

    eval map=\$$mapName

    for keyValuePair in ${map};
    do
        case "$keyValuePair" in
            --$key=*) value=`echo "$keyValuePair" | sed -e 's/^[^=]*=//'`
                      valueFound="true"
        esac
        if [ "$valueFound" == "true" ]; then break; fi
    done
    value=`echo $value | sed -e "s/:SP:/ /g"`
}

put "newMap" "name" "Irfan Zulfiqar"
put "newMap" "designation" "SSE"
put "newMap" "company" "My Own Company"

get "newMap" "company"
echo $value

get "newMap" "name"
echo $value

संपादित करें: सभी कुंजियों को लाने के लिए बस एक और तरीका जोड़ा गया।

getKeySet() {
    if [ "$#" != 1 ]; 
    then 
        exit 1; 
    fi

    mapName=$1; 

    eval map="\"\$$mapName\""

    keySet=`
           echo $map | 
           sed -e "s/=[^ ]*//g" -e "s/\([ ]*\)--/\1/g"
          `
}

1
आप evalडेटा को आईएनजी के रूप में यद्यपि यह बैश कोड है, और क्या अधिक है: आप इसे ठीक से उद्धृत करने में विफल। दोनों ही बगों की भीड़ और मनमाने कोड इंजेक्शन का कारण बनते हैं।
लहनाथ

3

बैश 3 के लिए, एक विशेष मामला है जिसका एक अच्छा और सरल समाधान है:

यदि आप बहुत सारे चर नहीं संभालना चाहते हैं, या कुंजियाँ केवल अमान्य चर पहचानकर्ता हैं, और आपके सरणी में 256 से कम आइटम होने की गारंटी है , तो आप फ़ंक्शन रिटर्न मान का दुरुपयोग कर सकते हैं। इस समाधान के लिए किसी उपधारा की आवश्यकता नहीं है क्योंकि मूल्य आसानी से एक चर के रूप में उपलब्ध है, और न ही कोई पुनरावृत्ति ताकि प्रदर्शन चिल्लाए। इसके अलावा यह बहुत पठनीय है, लगभग बैश 4 संस्करण की तरह।

यहाँ सबसे मूल संस्करण है:

hash_index() {
    case $1 in
        'foo') return 0;;
        'bar') return 1;;
        'baz') return 2;;
    esac
}

hash_vals=("foo_val"
           "bar_val"
           "baz_val");

hash_index "foo"
echo ${hash_vals[$?]}

याद रखें, एकल उद्धरणों का उपयोग करें case, अन्यथा यह ग्लोबिंग के अधीन है। शुरू से ही स्थिर / जमे हुए हैश के लिए वास्तव में उपयोगी है, लेकिन एक hash_keys=()सरणी से एक सूचकांक जनरेटर लिख सकता है ।

बाहर देखें, यह पहले वाले को डिफॉल्ट करता है, इसलिए आप शून्य तत्व को अलग करना चाहते हैं:

hash_index() {
    case $1 in
        'foo') return 1;;
        'bar') return 2;;
        'baz') return 3;;
    esac
}

hash_vals=("",           # sort of like returning null/nil for a non existent key
           "foo_val"
           "bar_val"
           "baz_val");

hash_index "foo" || echo ${hash_vals[$?]}  # It can't get more readable than this

कैविएट: लंबाई अब गलत है।

वैकल्पिक रूप से, यदि आप शून्य-आधारित इंडेक्सिंग रखना चाहते हैं, तो आप किसी अन्य इंडेक्स वैल्यू और गैर-मौजूद कुंजी के खिलाफ गार्ड रख सकते हैं, लेकिन यह कम पठनीय है:

hash_index() {
    case $1 in
        'foo') return 0;;
        'bar') return 1;;
        'baz') return 2;;
        *)   return 255;;
    esac
}

hash_vals=("foo_val"
           "bar_val"
           "baz_val");

hash_index "foo"
[[ $? -ne 255 ]] && echo ${hash_vals[$?]}

या, लंबाई को सही रखने के लिए, अनुक्रमणिका को एक से हटाएं:

hash_index() {
    case $1 in
        'foo') return 1;;
        'bar') return 2;;
        'baz') return 3;;
    esac
}

hash_vals=("foo_val"
           "bar_val"
           "baz_val");

hash_index "foo" || echo ${hash_vals[$(($? - 1))]}

2

आप डायनामिक वैरिएबल नामों का उपयोग कर सकते हैं और चर नामों को हैशमैप की कुंजी की तरह काम कर सकते हैं।

उदाहरण के लिए, यदि आपके पास उदाहरण कॉलम के रूप में दो कॉलम, नाम, क्रेडिट के साथ एक इनपुट फ़ाइल है, और आप प्रत्येक उपयोगकर्ता की आय का योग करना चाहते हैं:

Mary 100
John 200
Mary 50
John 300
Paul 100
Paul 400
David 100

आदेश bellow, कुंजी के रूप में गतिशील चरों का उपयोग के रूप में, सब कुछ योग होगा नक्शा _ $ {व्यक्ति} :

while read -r person money; ((map_$person+=$money)); done < <(cat INCOME_REPORT.log)

परिणाम पढ़ने के लिए:

set | grep map

आउटपुट होगा:

map_David=100
map_John=500
map_Mary=150
map_Paul=500

इन तकनीकों पर विस्तार करते हुए, मैं GitHub पर एक फ़ंक्शन विकसित कर रहा हूं, जो एक HashMap ऑब्जेक्ट , shell_map की तरह काम करता है ।

" HashMap उदाहरण " बनाने के लिए शेल_मैप फ़ंक्शन विभिन्न नामों के तहत स्वयं की प्रतियां बनाने में सक्षम है। प्रत्येक नई फ़ंक्शन कॉपी में एक अलग $ FUNCNAME चर होगा। $ FUNCNAME का उपयोग तब प्रत्येक मानचित्र उदाहरण के लिए नाम स्थान बनाने के लिए किया जाता है।

मैप कुंजियाँ वैश्विक चर हैं, $ FUNCNAME_DATA_ $ कुंजी के रूप में, जहाँ $ KEY मानचित्र में जोड़ी गई कुंजी है। ये चर गतिशील चर हैं

Bellow मैं इसका एक सरलीकृत संस्करण डालूँगा ताकि आप उदाहरण के रूप में उपयोग कर सकें।

#!/bin/bash

shell_map () {
    local METHOD="$1"

    case $METHOD in
    new)
        local NEW_MAP="$2"

        # loads shell_map function declaration
        test -n "$(declare -f shell_map)" || return

        # declares in the Global Scope a copy of shell_map, under a new name.
        eval "${_/shell_map/$2}"
    ;;
    put)
        local KEY="$2"  
        local VALUE="$3"

        # declares a variable in the global scope
        eval ${FUNCNAME}_DATA_${KEY}='$VALUE'
    ;;
    get)
        local KEY="$2"
        local VALUE="${FUNCNAME}_DATA_${KEY}"
        echo "${!VALUE}"
    ;;
    keys)
        declare | grep -Po "(?<=${FUNCNAME}_DATA_)\w+((?=\=))"
    ;;
    name)
        echo $FUNCNAME
    ;;
    contains_key)
        local KEY="$2"
        compgen -v ${FUNCNAME}_DATA_${KEY} > /dev/null && return 0 || return 1
    ;;
    clear_all)
        while read var; do  
            unset $var
        done < <(compgen -v ${FUNCNAME}_DATA_)
    ;;
    remove)
        local KEY="$2"
        unset ${FUNCNAME}_DATA_${KEY}
    ;;
    size)
        compgen -v ${FUNCNAME}_DATA_${KEY} | wc -l
    ;;
    *)
        echo "unsupported operation '$1'."
        return 1
    ;;
    esac
}

उपयोग:

shell_map new credit
credit put Mary 100
credit put John 200
for customer in `credit keys`; do 
    value=`credit get $customer`       
    echo "customer $customer has $value"
done
credit contains_key "Mary" && echo "Mary has credit!"

2

अभी तक एक और गैर-बैश -4 (यानी, बैश 3, मैक-संगत) तरीका:

val_of_key() {
    case $1 in
        'A1') echo 'aaa';;
        'B2') echo 'bbb';;
        'C3') echo 'ccc';;
        *) echo 'zzz';;
    esac
}

for x in 'A1' 'B2' 'C3' 'D4'; do
    y=$(val_of_key "$x")
    echo "$x => $y"
done

प्रिंटों:

A1 => aaa
B2 => bbb
C3 => ccc
D4 => zzz

साथ समारोह caseएक साहचर्य सरणी की तरह कार्य करता है। दुर्भाग्य से इसका उपयोग नहीं किया जा सकता है return, इसलिए echoइसके आउटपुट के लिए है, लेकिन यह एक समस्या नहीं है, जब तक कि आप एक शुद्धतावादी नहीं हैं जो उप-नक्षत्रों को त्यागने के लिए दूर है।


1

इससे पहले कि मैं इस सवाल पर दया नहीं करता था - मैंने लाइब्रेरी शेल-फ्रेमवर्क लिखा है जिसमें दूसरों के नक्शे (एसोसिएटिव एरे) शामिल हैं। इसका अंतिम संस्करण यहां पाया जा सकता है

उदाहरण:

#!/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")"

1

एक और विकल्प जोड़ना, अगर 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

मैंने इसे सही पाया है, जैसा कि पहले ही उल्लेख किया गया है, कि सबसे अच्छा प्रदर्शन करने का तरीका किसी फ़ाइल में कुंजी / वैल लिखना है, और फिर उन्हें पुनः प्राप्त करने के लिए grep / awk का उपयोग करें। यह अनावश्यक IO के सभी प्रकार की तरह लगता है, लेकिन डिस्क कैश में किक करता है और इसे बहुत ही कुशल बनाता है - उपरोक्त तरीकों में से एक का उपयोग करके मेमोरी में उन्हें स्टोर करने की कोशिश करने की तुलना में बहुत तेज़ है (जैसा कि बेंचमार्क शो)।

यहां मुझे एक त्वरित, स्वच्छ विधि पसंद है:

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`

यदि आप प्रति कुंजी एकल-मूल्य लागू करना चाहते हैं, तो आप hput () में थोड़ा grep / sed कार्रवाई भी कर सकते हैं।


0

कई साल पहले मैंने बैश के लिए स्क्रिप्ट लाइब्रेरी लिखी थी जो अन्य विशेषताओं (लॉगिंग, कॉन्फ़िगरेशन फ़ाइलों, कमांड लाइन तर्क के लिए विस्तारित समर्थन, सहायता, यूनिट परीक्षण, आदि) के बीच सहयोगी सरणियों का समर्थन करती थी। लाइब्रेरी में साहचर्य सरणियों के लिए एक आवरण होता है और स्वचालित रूप से उपयुक्त मॉडल (bash4 के लिए आंतरिक और पिछले संस्करणों के लिए अनुकरण) पर स्विच होता है। इसे शेल-फ्रेमवर्क कहा जाता था और इसकी उत्पत्ति .o.zz.ch पर की गई थी, लेकिन आज संसाधन बंद है। अगर किसी को अभी भी इसकी आवश्यकता है तो मैं इसे आपके साथ साझा कर सकता हूं।


हो सकता है कि इसे जीथुब पर चिपका दिया जाए
मार्क के कोवान

0

शेल में डेटा संरचना जैसी कोई अंतर्निहित मानचित्र नहीं है, मैं उस तरह की वस्तुओं का वर्णन करने के लिए कच्चे स्ट्रिंग का उपयोग करता हूं:

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

ऐसा लगता है कि अन्य लोगों के उत्तर की तुलना में चालाक नहीं है, लेकिन नए लोगों के लिए समझना आसान है।


-1

मैंने निम्नलिखित के साथ वादिम के समाधान को संशोधित किया:

####################################################################
# 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 के लिए है, हालांकि इसका साइड-इफ़ेक्ट यह है कि यह मौन रूप से गुम हुए नक्शों को नज़रअंदाज़ कर देगा, लेकिन यह मेरे उपयोग के मामले को बेहतर बनाता है क्योंकि मैं अभी एक लूप में आइटम को छोड़ने के लिए एक कुंजी की जांच करना चाहता था।


-1

देर से जवाब, लेकिन इस तरह से समस्या को संबोधित करने पर विचार करें, इस प्रकार के रूप में पढ़े जाने वाले एक 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
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.