Array asosiatif dalam skrip Shell

116

Kami memerlukan skrip yang mensimulasikan array Asosiatif atau Peta seperti struktur data untuk Skrip Shell, badan apa saja?

Irfan Zulfiqar
sumber

Jawaban:

20

Untuk menambah jawaban Irfan , berikut adalah versi yang lebih pendek dan lebih cepat get()karena tidak memerlukan iterasi atas isi peta:

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

    map=${!mapName}
    value="$(echo $map |sed -e "s/.*--${key}=\([^ ]*\).*/\1/" -e 's/:SP:/ /g' )"
}
Jerry Penner
sumber
16
garpu subkulit dan sed hampir tidak optimal. Bash4 mendukung ini secara native dan bash3 memiliki alternatif yang lebih baik.
lhunath
149

Pilihan lain, jika portabilitas bukan perhatian utama Anda, adalah menggunakan array asosiatif yang dibangun di dalam shell. Ini seharusnya berfungsi di bash 4.0 (sekarang tersedia di sebagian besar distro utama, meskipun tidak di OS X kecuali Anda menginstalnya sendiri), ksh, dan zsh:

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

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

Tergantung pada shell, Anda mungkin perlu melakukan typeset -A newmapbukan declare -A newmap, atau dalam beberapa hal mungkin tidak diperlukan sama sekali.

Brian Campbell
sumber
Terima kasih untuk jawaban Anda, saya pikir itu adalah cara terbaik untuk melakukannya untuk orang-orang yang akan menggunakan bash 4.0 atau lebih tinggi.
Irfan Zulfiqar
Saya akan menambahkan sedikit kludge untuk memastikan BASH_VERSION disetel, dan> = 4. Dan ya, BASH 4 benar-benar keren!
Pos Tim
Saya menggunakan sesuatu seperti ini. Apa cara terbaik untuk "menangkap" kesalahan di mana indeks / subskrip array tidak ada? Misalnya, bagaimana jika saya menggunakan subskrip sebagai opsi baris perintah, dan pengguna salah ketik dan memasukkan "designatio"? Saya mendapatkan kesalahan "subskrip array buruk" tetapi tidak dapat memvalidasi input pada saat pencarian array, jika itu memungkinkan?
Yer
3
@Jer Ini cukup tidak jelas, tetapi untuk menentukan apakah sebuah variabel disetel di shell, Anda dapat menggunakan test -z ${variable+x}( xtidak masalah, itu bisa berupa string apa pun). Untuk array asosiatif di Bash, Anda bisa melakukan hal serupa; gunakan test -z ${map[key]+x}.
Brian Campbell
95

Cara 4 non-bash lainnya.

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

Anda juga bisa melempar pernyataan if untuk mencari di sana. jika [[$ var = ~ / blah /]]. atau terserah.

Bubnoff
sumber
2
Metode ini bagus jika Anda tidak memiliki Bash 4. Tapi saya pikir baris yang mengambil NILAI akan lebih aman seperti ini: VALUE = $ {hewan # *:}. Dengan hanya satu # karakter, pencocokan akan berhenti di ":" pertama. Itu memungkinkan nilai berisi ":" juga.
Ced-le-pingouin
@ Ced-le-pingouin ~ Itu poin yang bagus! Saya tidak mengerti. Saya telah mengedit posting saya untuk mencerminkan peningkatan yang Anda sarankan.
Bubnoff
1
Ini adalah emulasi yang cukup hack dari array asosiatif menggunakan substitusi parameter BASH. Param-sub "key" menggantikan segala sesuatu sebelum titik dua dan pola nilai menggantikan semuanya setelah titik dua. Mirip dengan pertandingan wildcard regex. Jadi BUKAN array asosiatif yang benar. Tidak disarankan kecuali Anda memerlukan cara yang mudah dipahami untuk melakukan fungsi hash / asosiatif seperti array di BASH 3 atau lebih rendah. Itu berhasil! Selengkapnya di sini: tldp.org/LDP/abs/html/parameter-substitution.html#PSOREX2
Bubnoff
1
Ini tidak mengimplementasikan array asosiatif karena tidak menyediakan cara untuk mencari item dengan kunci. Ini hanya menyediakan cara untuk menemukan setiap kunci (dan nilai) dari indeks numerik. (Item dapat ditemukan dengan kunci dengan melakukan iterasi melalui array, tetapi bukan itu yang diinginkan untuk array asosiatif.)
Eric Postpischil
@Tokopedia Ini hanya peretasan. Ini memungkinkan seseorang untuk menggunakan sintaks yang sudah dikenal dalam pengaturan tetapi masih membutuhkan iterasi melalui array seperti yang Anda katakan. Saya telah mencoba memperjelas dalam komentar saya sebelumnya bahwa itu jelas bukan array asosiatif dan saya bahkan tidak merekomendasikannya jika Anda memiliki alternatif. Satu-satunya hal yang menguntungkannya, dalam pandangan saya, adalah mudah untuk ditulis dan digunakan bagi mereka yang akrab dengan bahasa lain seperti Python. Jika Anda berada pada titik di mana Anda benar-benar ingin mengimplementasikan array asosiatif di BASH 3, maka Anda mungkin perlu sedikit menelusuri kembali langkah-langkah Anda.
Bubnoff
34

Saya pikir Anda perlu melangkah mundur dan memikirkan tentang apa itu peta, atau array asosiatif, sebenarnya. Semua itu adalah cara untuk menyimpan nilai untuk kunci tertentu, dan mendapatkan kembali nilai itu dengan cepat dan efisien. Anda mungkin juga ingin dapat mengulang kunci untuk mengambil setiap pasangan nilai kunci, atau menghapus kunci dan nilai yang terkait.

Sekarang, pikirkan tentang struktur data yang Anda gunakan sepanjang waktu dalam skrip shell, dan bahkan hanya di shell tanpa menulis skrip, yang memiliki properti ini. Bingung? Itu sistem file.

Sungguh, yang Anda butuhkan untuk memiliki array asosiatif dalam pemrograman shell adalah direktori temp. mktemp -dadalah konstruktor array asosiatif Anda:

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

Jika Anda tidak ingin menggunakan echodan cat, Anda selalu dapat menulis bungkus kecil; yang ini dimodelkan dari Irfan, meskipun mereka hanya menampilkan nilai daripada menyetel variabel arbitrer seperti $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

sunting : Pendekatan ini sebenarnya sedikit lebih cepat daripada pencarian linier menggunakan sed yang disarankan oleh penanya, serta lebih kuat (memungkinkan kunci dan nilai untuk memuat -, =, spasi, qnd ": SP:"). Fakta bahwa ia menggunakan sistem berkas tidak membuatnya lambat; file-file ini sebenarnya tidak pernah dijamin akan ditulis ke disk kecuali Anda memanggil sync; untuk file sementara seperti ini dengan masa hidup yang singkat, bukan tidak mungkin banyak dari mereka tidak akan pernah ditulis ke disk.

Saya melakukan beberapa tolok ukur kode Irfan, modifikasi kode Irfan oleh Jerry, dan kode saya, menggunakan program driver berikut:

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

Hasil:

    $ time ./driver.sh irfan 10 5

    0m0.975s nyata
    pengguna 0m0.280s
    sys 0m0.691s

    $ time ./driver.sh brian 10 5

    0m0.226s nyata
    pengguna 0m0.057s
    sys 0m0.123s

    $ waktu ./driver.sh jerry 10 5

    0m0.706s nyata
    pengguna 0m0.228s
    sys 0m0.530s

    $ waktu ./driver.sh irfan 100 5

    0m10.633s nyata
    pengguna 0m4.366s
    sys 0m7.127s

    $ time ./driver.sh brian 100 5

    0m1.682s nyata
    pengguna 0m0.546s
    sys 0m1.082s

    $ waktu ./driver.sh jerry 100 5

    0m9.315 nyata
    pengguna 0m4.565s
    sys 0m5.446s

    $ time ./driver.sh irfan 10 500

    nyata 1m46.197s
    pengguna 0m44.869s
    sys 1m12.282s

    $ time ./driver.sh brian 10 500

    0m16.003 nyata
    pengguna 0m5.135s
    sys 0m10.396s

    $ waktu ./driver.sh jerry 10 500

    nyata 1m24,414s
    pengguna 0m39.696s
    sys 0m54.834s

    $ waktu ./driver.sh irfan 1000 5

    4m25.145s nyata
    pengguna 3m17.286s
    sys 1m21.490s

    $ time ./driver.sh brian 1000 5

    0m19.442s nyata
    pengguna 0m5.287s
    sys 0m10.751s

    $ waktu ./driver.sh jerry 1000 5

    nyata 5m29.136s
    pengguna 4m48.926s
    sys 0m59.336s

Brian Campbell
sumber
1
Saya tidak berpikir Anda harus menggunakan sistem file untuk peta, yang pada dasarnya menggunakan IO untuk sesuatu yang dapat Anda lakukan dengan cukup cepat di memori.
Irfan Zulfiqar
9
File tidak akan pernah ditulis ke disk; kecuali Anda memanggil sinkronisasi, sistem operasi mungkin membiarkannya di memori. Kode Anda memanggil sed dan melakukan beberapa pencarian linier, yang semuanya sangat lambat. Saya melakukan beberapa benchmark cepat, dan versi saya 5-35 kali lebih cepat.
Brian Campbell
di sisi lain, array asli bash4 secara signifikan merupakan pendekatan yang lebih baik dan di bash3 Anda masih dapat menyimpan semuanya dari disk tanpa melakukan forking dengan menggunakan deklarasi dan tipuan.
lhunath
7
"fast" dan "shell" tidak benar-benar cocok: tentu saja bukan untuk masalah kecepatan yang kita bicarakan di level "hindari miniscule IO". Anda dapat mencari dan menggunakan / dev / shm untuk menjamin tidak ada IO.
jmtd
2
Solusi ini membuat saya takjub, dan luar biasa. Masih berlaku di 2016. Itu benar-benar harus menjadi jawaban yang diterima.
Gordon
7

Bash4 mendukung ini secara native. Jangan gunakan grepatau eval, mereka adalah peretasan paling jelek.

Untuk jawaban verbose dan mendetail dengan kode contoh lihat: /programming/3467959

lhunath
sumber
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; }'
}

Contoh:

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
Vadim
sumber
4

Sekarang menjawab pertanyaan ini.

Skrip berikut mensimulasikan array asosiatif dalam skrip shell. Sederhana dan sangat mudah dimengerti.

Peta tidak lain adalah string yang tidak pernah berakhir yang memiliki keyValuePair yang disimpan sebagai --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company

spasi diganti dengan ': SP:' untuk nilai

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

edit: Baru saja menambahkan metode lain untuk mengambil semua kunci.

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

    mapName=$1; 

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

    keySet=`
           echo $map | 
           sed -e "s/=[^ ]*//g" -e "s/\([ ]*\)--/\1/g"
          `
}
Irfan Zulfiqar
sumber
1
Anda sedang evalmengumpulkan data seolah-olah itu adalah kode bash, dan terlebih lagi: Anda gagal mengutipnya dengan benar. Keduanya menyebabkan banyak bug dan injeksi kode arbitrer.
lhunath
3

Untuk Bash 3, ada kasus tertentu yang memiliki solusi yang bagus dan sederhana:

Jika Anda tidak ingin menangani banyak variabel, atau kunci hanyalah pengenal variabel yang tidak valid, dan array Anda dijamin memiliki kurang dari 256 item , Anda dapat menyalahgunakan nilai pengembalian fungsi. Solusi ini tidak memerlukan subkulit karena nilainya sudah tersedia sebagai variabel, atau iterasi apa pun sehingga performa menjerit. Juga sangat mudah dibaca, hampir seperti versi Bash 4.

Ini versi paling dasar:

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[$?]}

Ingat, gunakan tanda kutip tunggal dalam case, jika tidak, itu tunduk pada globbing. Sangat berguna untuk hash statis / beku sejak awal, tetapi seseorang dapat menulis generator indeks dari hash_keys=()array.

Hati-hati, defaultnya adalah yang pertama, jadi Anda mungkin ingin menyisihkan elemen ke nol:

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

Peringatan: panjangnya sekarang salah.

Atau, jika Anda ingin mempertahankan pengindeksan berbasis nol, Anda dapat memesan nilai indeks lain dan menjaga dari kunci yang tidak ada, tetapi kurang terbaca:

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[$?]}

Atau, untuk menjaga panjangnya tetap benar, offset indeks satu per satu:

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))]}
Lloeki
sumber
2

Anda dapat menggunakan nama variabel dinamis dan membiarkan nama variabel berfungsi seperti kunci dari hashmap.

Misalnya, jika Anda memiliki file input dengan dua kolom, nama, kredit, seperti contoh di bawah ini, dan Anda ingin menjumlahkan pendapatan setiap pengguna:

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

Perintah di bawah ini akan menjumlahkan semuanya, menggunakan variabel dinamis sebagai kunci, dalam bentuk peta _ $ {person} :

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

Untuk membaca hasilnya:

set | grep map

Outputnya adalah:

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

Menguraikan teknik ini, saya mengembangkan di GitHub sebuah fungsi yang bekerja seperti Objek HashMap , shell_map .

Untuk membuat " instance HashMap ", fungsi shell_map dapat membuat salinannya sendiri dengan nama yang berbeda. Setiap salinan fungsi baru akan memiliki variabel $ FUNCNAME yang berbeda. $ FUNCNAME kemudian digunakan untuk membuat namespace untuk setiap instance Peta.

Kunci peta adalah variabel global, dalam bentuk $ FUNCNAME_DATA_ $ KEY, di mana $ KEY adalah kunci yang ditambahkan ke Peta. Variabel ini adalah variabel dinamis .

Di bawah ini saya akan memberikan versi yang disederhanakan sehingga Anda dapat menggunakan sebagai contoh.

#!/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
}

Pemakaian:

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!"
Bruno Negrão Zica
sumber
2

Namun cara non-bash-4 lainnya (yaitu, bash 3, kompatibel dengan Mac):

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

Cetakan:

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

Fungsi dengan casetindakan seperti array asosiatif. Sayangnya itu tidak bisa digunakan return, jadi harus echooutputnya, tapi ini bukan masalah, kecuali Anda seorang purist yang menghindari forking subkulit.

Walter Tross
sumber
1

Sayang sekali saya tidak melihat pertanyaan sebelumnya - saya telah menulis library shell-framework yang berisi antara lain maps (Associative arrays). Versi terakhirnya dapat ditemukan di sini .

Contoh:

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

Menambahkan opsi lain, jika jq tersedia:

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 '"' 
kritik
sumber
0

Saya merasa benar, seperti yang telah disebutkan, bahwa metode berkinerja terbaik adalah menulis kunci / vals ke file, dan kemudian menggunakan grep / awk untuk mengambilnya. Kedengarannya seperti semua jenis IO yang tidak perlu, tetapi cache disk masuk dan membuatnya sangat efisien - jauh lebih cepat daripada mencoba menyimpannya dalam memori menggunakan salah satu metode di atas (seperti yang ditunjukkan oleh benchmark).

Inilah metode cepat dan bersih yang saya suka:

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`

Jika Anda ingin menerapkan nilai tunggal per kunci, Anda juga dapat melakukan sedikit tindakan grep / sed di hput ().

Al P.
sumber
0

Beberapa tahun yang lalu saya menulis pustaka skrip untuk bash yang mendukung array asosiatif di antara fitur-fitur lain (logging, file konfigurasi, dukungan tambahan untuk argumen baris perintah, menghasilkan bantuan, pengujian unit, dll). Pustaka berisi pembungkus untuk array asosiatif dan secara otomatis beralih ke model yang sesuai (internal untuk bash4 dan meniru untuk versi sebelumnya). Itu disebut kerangka-shell dan dihosting di origo.ethz.ch tetapi hari ini sumber daya ditutup. Jika seseorang masih membutuhkannya, saya dapat membagikannya dengan Anda.

Pengemis
sumber
Mungkin layak untuk tetap bertahan di github
Mark K Cowan
0

Shell tidak memiliki peta bawaan seperti struktur data, saya menggunakan string mentah untuk mendeskripsikan item seperti itu:

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

ketika mengekstrak item dan atributnya:

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

Ini sepertinya tidak pandai dari jawaban orang lain, tetapi mudah dimengerti untuk orang baru untuk dikupas.

coanor
sumber
-1

Saya memodifikasi solusi Vadim dengan yang berikut:

####################################################################
# 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; }'
}

Perubahannya adalah map_get untuk mencegahnya mengembalikan kesalahan jika Anda meminta kunci yang tidak ada, meskipun efek sampingnya adalah ia juga akan secara diam-diam mengabaikan peta yang hilang, tetapi lebih cocok untuk kasus penggunaan saya karena saya baru saja ingin memeriksa kunci untuk melewatkan item dalam satu lingkaran.

Haravikk
sumber
-1

Balas terlambat, tetapi pertimbangkan untuk mengatasi masalah dengan cara ini, menggunakan bash builtin yang dibaca seperti yang diilustrasikan dalam cuplikan kode dari skrip firewall ufw yang mengikuti. Pendekatan ini memiliki keuntungan menggunakan sebanyak mungkin kumpulan bidang yang dibatasi (tidak hanya 2) seperti yang diinginkan. Kami telah menggunakan | pembatas karena penentu kisaran port mungkin memerlukan titik dua, yaitu 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
AsymLabs
sumber