Kaip apibrėžti maišos lenteles bash?

Kas yra Python žodynų ekvivalentas, tačiau Bash'e (turėtų veikti per OS X ir Linux).

416
29 сент. Sridhar Ratnakumar nustatė rugsėjo 29 d. 2009-09-29 21:29 '09 21:29 2009-09-29 21:29
@ 16 atsakymų

Bash 4

Bash 4 iš pradžių palaiko šią funkciją. Įsitikinkite, kad jūsų „hashbang“ scenarijus yra #!/usr/bin/env bash arba #!/bin/bash arba kažkas, kas reiškia „ bash ir ne „ sh . Įsitikinkite, kad vykdote savo scenarijų, o ne atlikite kažką kvailo, pvz., „ sh script kurį jūsų „ bash hashbang“ ignoruos. Tai yra pagrindiniai dalykai, tačiau daugelis jų nepavyksta, todėl jie kartojasi.

Jūs paskelbiate asociatyviąją masyvą vykdydami:

 declare -A animals 

Jį galite užpildyti elementais, naudodami įprastą masyvo priskyrimo operatorių:

 animals=( ["moo"]="cow" ["woof"]="dog") 

Arba juos sujungti:

 declare -A animals=( ["moo"]="cow" ["woof"]="dog") 

Tada naudokite juos kaip paprastas matricas. animals['key']='value' norėdami nustatyti vertę, "${animals[@]}" plečia vertes, "${!animals[@]}" (pastaba ! ) plečia raktus. Nepamirškite jų cituoti:

 echo "${animals[moo]}" for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done 

Bash 3

Prieš bash 4, neturėjote asociatyvių matricų. Nenaudokite eval jiems imituoti . Turėtumėte vengti eval, kaip maras, nes tai yra lukštinių scenarijų maras. Svarbiausia priežastis yra ta, kad nenorite apdoroti duomenų kaip vykdomąjį kodą (yra daug kitų priežasčių).

Visų pirma: tiesiog pagalvokite apie atnaujinimą į „bash 4.“. Dabar ateitis, nustoti gyventi praeityje ir kenčia nuo jo , todėl kvaila pertrauka ir bjaurus, kad sulaužytumėte savo kodą, ir kiekviena neturtinga siela yra įstrigo, remdama ją.

Jei turite tam tikrą kvailą pasiteisinimą, kodėl „negalite atnaujinti“, declare tai yra daug saugesnis variantas. Jis neišnagrinėja duomenų taip pat, kaip ir „bash“ kodas, kaip ir eval , todėl jis neleidžia įvesti savavališko kodo taip lengvai.

Paruoškime atsakymą įvesdami sąvokas:

Pirma, netiesioginis gydymas (rimtai; niekada nenaudokite, nebent esate psichiškai serga, ar neturite kito blogo pasiteisinimo rašyti hacks).

 $ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}" cow 

Antra, declare :

 $ sound=moo; animal=cow; declare "animals_$sound=$animal"; echo "$animals_moo" cow 

Įdėkite juos kartu:

 # Set a value: declare "array_$index=$value" # Get a value: arrayGet() { local array=$1 index=$2 local i="${array}_$index" printf '%s' "${!i}" } 

Leiskite naudoti:

 $ sound=moo $ animal=cow $ declare "animals_$sound=$animal" $ arrayGet animals "$sound" cow 

Pastaba: declare negalima įdėti į funkciją. Bet koks declare bash funkcijos viduje paverčia jo sukurtą kintamąjį į šios funkcijos sritį, o tai reiškia, kad negalime pasiekti ar keisti pasaulinių matricų. (Bash 4, galite naudoti -g deklaraciją deklaruoti pasaulinius kintamuosius - bet bash 4, pirmiausia turėtumėte naudoti asociatyviąsias matricas, o ne šį įsilaužimą.)

Santrauka

Atnaujinkite savo sistemą į „bash 4“ ir naudokite declare -A . Jei negalite, pagalvokite apie perjungimą į „ awk prieš atlikdami bjaurius „hacks“, kaip aprašyta aukščiau. Ir, be abejo, lieka vargšų, ištrauktų iš jaučių.

707
12 авг. atsakymas, kurį pateikė lhunath 12 rug . 2010-08-12 16:09 '10, 08:09 PM 2010-08-12 16:09

Čia yra parametrų pakeitimas, nors jis gali būti ne PC ... pavyzdžiui, netiesioginis.

 #!/bin/bash # Array pretending to be a Pythonic dictionary 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 printf "%s is an extinct animal which likes to %s\n" "${ARRAY[1]%%:*}" "${ARRAY[1]##*:}" 
border=0

Žinoma, BASH 4 yra geresnis, bet jei jums reikia įsilaužimo, bus tik įsilaužimas. Galite ieškoti masyvo / maišos su panašiais metodais.

98
15 дек. atsakymą pateikė Bubnoff gruodžio 15 d 2010-12-15 01:02 '10 ne 1:02 2010-12-15 01:02

Štai ką ieškojau čia:

 declare -A hashmap hashmap["key"]="value" hashmap["key2"]="value2" echo "${hashmap["key"]}" for key in ${!hashmap[@]}; do echo $key; done for value in ${hashmap[@]}; do echo $value; done echo hashmap has ${#hashmap[@]} elements 

Tai nepadėjo man su bash 4.1.5:

 animals=( ["moo"]="cow" ) 
52
23 мая '11 в 3:30 2011-05-23 03:30 atsakymas suteiktas aktivb gegužės 23 d., 11 val. 3:30 val. 2011-05-23 03:30

Papildomai galite keisti įvesties () / hget () sąsają taip, kad pavadintumėte skliautus taip:

 hput() { eval "$1""$2"='$3' } hget() { eval echo '${'"$1$2"'#hash}' } 

ir tada

 hput capitals France Paris hput capitals Netherlands Amsterdam hput capitals Spain Madrid echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain` 

Tai leidžia nustatyti kitus žemėlapius, kurie neprieštarauja (pavyzdžiui, „rcapitals“, kuri ieško šalies sostinėje). Bet, bet kuriuo atveju, manau, jūs pastebėsite, kad visa tai yra gana baisu dėl veiklos.

Jei tikrai reikia greito maišymo paieškos, yra baisus, baisus įsilaužimas, kuris tikrai veikia labai gerai. Taip: rašykite raktą / vertes laikinajame faile, po vieną eilutę, tada naudokite „grep“ ^ $ klavišą, kad gautumėte juos naudodami kanalus su iškirptais arba „awk“ ar „sed“ ar kitais būdais, kad gautumėte vertes.

Kaip sakiau, tai skamba siaubingai, ir atrodo, kad ji turėtų būti lėta ir padaryti visus nereikalingus I / O įrašus, tačiau praktiškai tai labai greita (disko talpykla yra nuostabi, ar ne?), Net ir labai dideliems maišams - lentelės Jūs turite pateikti pagrindinį unikalumą ir tt Net jei turite tik kelis šimtus įrašų, išvesties / grep kombinacinis failas bus gana greitas - mano patirtis kelis kartus greičiau. Jis taip pat sunaudoja mažiau atminties.

Štai vienas iš būdų tai padaryti:

 hinit() { rm -f /tmp/hashmap.$1 } hput() { echo "$2 $3" >> /tmp/hashmap.$1 } hget() { grep "^$2 " /tmp/hashmap.$1 | awk '{ print $2 };' } hinit capitals hput capitals France Paris hput capitals Netherlands Amsterdam hput capitals Spain Madrid echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain` 
22
09 февр. Atsakymą pateikė Al P. 09 Feb. 2010-02-09 02:38 '10, 2:38, 2010-02-09 02:38
 hput () { eval hash"$1"='$2' } hget () { eval echo '${hash'"$1"'#hash}' } hput France Paris hput Netherlands Amsterdam hput Spain Madrid echo `hget France` and `hget Netherlands` and `hget Spain` 

 $ sh hash.sh Paris and Amsterdam and Madrid 
13
30 сент. „ DigitalRoss“ atsakymas rugsėjo 30 d 2009-09-30 01:45 '09, 1:45 val 2009-09-30 01:45

Tiesiog naudokite failų sistemą.

Failų sistema yra medžio struktūra, kuri gali būti naudojama kaip maišos žemėlapis. Jūsų maišos lentelė bus laikinas katalogas, raktai bus failų pavadinimai, o jūsų vertės bus failo turinys. Privalumas yra tai, kad jis gali tvarkyti didžiules maišos korteles ir nereikalauja specialaus apvalkalo.

Sukurti maišos lentelę

hashtable=$(mktemp -d)

Pridėti elementą

echo $value > $hashtable/$key

Skaityti elementą

value=$(< $hashtable/$key)

Našumas

Žinoma, tai lėtas, bet ne toks lėtas. Aš išbandžiau jį savo mašinoje, naudojant SSD ir btrfs , ir tai daro apie 3000 skaitymo / rašymo per sekundę .

10
06 дек. lovasoa atsakymas 06 dec. 2016-12-06 18:36 '16 at 18:36 2016-12-06 18:36

Apsvarstykite sprendimą, naudodami integruotą „bash“ skaitymą , kaip parodyta kodo fragmente iš toliau pateiktos „ufw“ scenarijaus užkardos. Šio požiūrio privalumas yra kuo daugiau laukų rinkinių (ne tik 2). Mes naudojome separatorių Kadangi uosto diapazono specifikai gali reikėti dvitaškio, ty 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 
9
15 сент. Atsakymą pateikė AsymLabs 15 Sep. 2015-09-15 20:16 '15 , 20:16 2015-09-15 20:16

Sutinku su @ lhunath ir kitais, kad asociatyvi masyvas yra Bash 4 kelias. Jei esate susietas su „Bash 3“ (OSX, seni „distros“, kurių negalite atnaujinti), galite naudoti ir expr, kuris turėtų būti visur, linija ir reguliarus išraiškos. Man ypač patinka, kai žodynas nėra per didelis.

  • Pasirinkite du ribotuvus, kurių nenaudosite klavišuose ir vertėse (pvz., „,“ Ir „:“)
  • Užrašykite savo kortelę kaip eilutę (pažymėkite separatorių "," taip pat pradžioje ir pabaigoje)

     animals=",moo:cow,woof:dog," 
  • Naudokite įprastą išraišką, kad gautumėte vertes.

     get_animal { echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")" } 
  • Padalinkite eilutę, kad pateiktumėte elementus

     get_animal_items { arr=$(echo "${animals:1:${#animals}-2}" | tr "," "\n") for i in $arr do value="${i##*:}" key="${i%%:*}" echo "${value} likes to $key" done } 

Dabar galite jį naudoti:

 $ animal = get_animal "moo" cow $ get_animal_items cow likes to moo dog likes to woof 
6
18 апр. atsakymas pateikiamas kovo 18 d. 2014-04-18 02:05 '14, 02:05 2014-04-18 02:05

Man labai patiko „Al P“ atsakymas, bet norėjau, kad unikalumas būtų pigus, taigi aš dar vieną žingsnį ėmiau - naudokite katalogą. Yra keletas akivaizdžių apribojimų (katalogų failų apribojimai, negaliojantys failų pavadinimai), tačiau dažniausiai jie turėtų veikti.

 hinit() { rm -rf /tmp/hashmap.$1 mkdir -p /tmp/hashmap.$1 } hput() { printf "$3" > /tmp/hashmap.$1/$2 } hget() { cat /tmp/hashmap.$1/$2 } hkeys() { ls -1 /tmp/hashmap.$1 } hdestroy() { rm -rf /tmp/hashmap.$1 } hinit ids for (( i = 0; i < 10000; i++ )); do hput ids "key$i" "value$i" done for (( i = 0; i < 10000; i++ )); do printf '%s\n' $(hget ids "key$i") > /dev/null done hdestroy ids 

Tai taip pat šiek tiek geriau mano bandymuose.

 $ time bash hash.sh real 0m46.500s user 0m16.767s sys 0m51.473s $ time bash dirhash.sh real 0m35.875s user 0m8.002s sys 0m24.666s 

Tik maniau, kad galėčiau. Sveikinimai!

Redaguoti: pridėti hdestroy ()

5
28 окт. Cole Stanfield atsakė į spalio 28 d. 2010-10-28 21:36 '10, 21:36, 2010-10-28 21:36

Iki bash 4, nėra tinkamo būdo naudoti asociatyvias matricas bash. Geriausia naudoti interpretuotą kalbą, kuri iš tikrųjų palaiko tokius dalykus kaip awk. Kita vertus, bash 4 juos palaiko.

Kalbant apie mažiau gerus būdus „bash 3“, čia yra nuoroda, kuri gali padėti: http://mywiki.wooledge.org/BashFAQ/006

2
12 авг. kojiro atsakymas 12 rug . 2010-08-12 15:53 '10, 15:53, 2010-08-12 15:53

Dviejų dalykų, jūs galite naudoti atmintį vietoj / tmp bet kuriame 2.6 branduolyje, naudodami / dev / shm (Redhat). Be to, „Hget“ gali būti panaikinta naudojant šiuos veiksmus:

 function hget { while read key idx do if [ $key = $2 ] then echo $idx return fi done < /dev/shm/hashmap.$1 } 

Be to, darant prielaidą, kad visi raktai yra unikalūs, grąžinimas uždaro skaitymo ciklą ir neleidžia skaityti visų įrašų. Jei jūsų įgyvendinimas gali turėti pasikartojančius raktus, tiesiog palikite jį nepažymėtą. Tai taupo skaitymą ir atrakinimą tiek „grep“, tiek „awk“. Naudojant / dev / shm abiem įgyvendinimams, buvo panaudotas toks Hget Time naudojimas 3 maišos maišais ieškant paskutinio įrašo:

Grep / awk:

 hget() { grep "^$2 " /dev/shm/hashmap.$1 | awk '{ print $2 };' } $ time echo $(hget FD oracle) 3 real 0m0.011s user 0m0.002s sys 0m0.013s 

Skaityti / Atsakymas:

 $ time echo $(hget FD oracle) 3 real 0m0.004s user 0m0.000s sys 0m0.004s 

su keliais skambučiais, aš niekada nematiau mažiau nei 50% pagerėjimo. Tai galima paaiškinti su šakute virš galvos dėl /dev/shm .

2
15 авг. atsakymas, kurį pateikė jrichard 15 rug . 2010-08-15 02:45 '10, 02:45 val. 2010-08-15 02:45

Bash 3:

Skaitydami kai kuriuos atsakymus, sudėjau nedidelę funkciją, kurią norėčiau sugrąžinti, o tai gali padėti kitiems.

 # Define a hash like this MYHASH=("firstName:Milan" "lastName:Adamovsky") # Function to get value by key getHashKey() { declare -a hash=("${!1}") local key local lookup=$2 for key in "${hash[@]}" ; do KEY=${key%%:*} VALUE=${key#*:} if [[ $KEY == $lookup ]] then echo $VALUE fi done } # Function to get a list of all keys getHashKeys() { declare -a hash=("${!1}") local KEY local VALUE local key local lookup=$2 for key in "${hash[@]}" ; do KEY=${key%%:*} VALUE=${key#*:} keys+="${KEY} " done echo $keys } # Here we want to get the value of 'lastName' echo $(getHashKey MYHASH[@] "lastName") # Here we want to get all keys echo $(getHashKeys MYHASH[@]) 
2
29 авг. Milan Adamovsky atsakymas 29 rugpjūtis 2013-08-29 18:26 '13, 18:26 pm 2013-08-29 18:26

Darbuotojas ką tik paminėjo šią temą. Aš savarankiškai įgyvendina maišos lenteles bash'e, ir tai nepriklauso nuo 4 versijos. Nuo mano tinklaraščio 2010 m. Kovo mėn.

1
18 окт. Adomo Katzo atsakymas spalio 18 d 2012-10-18 03:39 '12 at 3:39 2012-10-18 03:39

„HashMaps“ sukuriu bash 3 naudojant dinaminius kintamuosius. Aš paaiškinau, kaip tai veikia mano atsakyme į: Asociatyvines matricas korpuso scenarijuose

Taip pat galite pažvelgti į shell_map , kuris yra „HashMap“ įgyvendinimas, atliekamas „bash 3“.

0
03 июня '16 в 19:34 2016-06-03 19:34 atsakymas į Bruno Negrão Zica pateikiamas birželio 13 d. 16 d. 19:34 2016-06-03 19:34

Norėdami gauti šiek tiek daugiau rezultatų, nepamirškite, kad „grep“ turi sustabdymo funkciją, kad sustabdytų, kai jis suranda n-ą rungtynę, šiuo atveju n bus lygus 1.

grep --max_count = 1 ... arba grep -m 1 ...

0
01 апр. atsakymas duodamas po balandžio 1 d. 2010-04-01 18:47 '10, 18:47, 2010-04-01 18:47

Aš taip pat naudoju bash4 metodą, bet aš pastebiu ir erzina klaidą.

Man reikėjo dinamiškai atnaujinti asociatyviosios masyvo turinį, todėl naudoju šį metodą:

 for instanceId in $instanceList do aws cloudwatch describe-alarms --output json --alarm-name-prefix $instanceId| jq '.["MetricAlarms"][].StateValue'| xargs | grep -E 'ALARM|INSUFFICIENT_DATA' [ $? -eq 0 ]  statusCheck+=([$instanceId]="checkKO") || statusCheck+=([$instanceId]="allCheckOk" done 

Aš sužinojau, kad su bash 4.3.11 pridedant prie dikto esamo rakto, pridėta vertė, jei ji jau yra. Taigi, pavyzdžiui, po tam tikro kartojimo, vertės turinys buvo "checkKOcheckKOallCheckOK", ir tai buvo blogai.

Nėra jokių problemų, susijusių su „bash 4.3.39“, kai esamo rakto pridėjimas reiškia faktinės vertės pakeitimą, jei jis jau yra.

Nusprendžiau tiesiog išvalyti / deklaruoti asociatyvų statuso tikrinimo matricą priešais ciklą:

 unset statusCheck; declare -A statusCheck 
0
04 сент. atsakymas pateikiamas Alex 04 sep. 2015-09-04 09:31 '15 at 9:31 am 2015-09-04 09:31