Python sözlüklerinin eşdeğeri nedir ancak Bash'te (OS X ve Linux'ta çalışmalıdır).
Python sözlüklerinin eşdeğeri nedir ancak Bash'te (OS X ve Linux'ta çalışmalıdır).
Yanıtlar:
Bash 4 bu özelliği doğal olarak desteklemektedir. Senaryonuzun hashbang olduğundan emin olun #!/usr/bin/env bash
ya da #!/bin/bash
kullanarak bitirmek olmaz sh
. Ya doğrudan scriptinizi emin olun veya yürütme script
ile bash script
. (Aslında Bash ile bir Bash komut dosyası yürütme değil gelmez olur ve olacak gerçekten kafa karıştırıcı!)
Aşağıdakileri yaparak ilişkilendirilebilir bir dizi bildirirsiniz:
declare -A animals
Normal dizi atama işlecini kullanarak bunu öğelerle doldurabilirsiniz. Örneğin, aşağıdakilerin haritasını çıkarmak istiyorsanız animal[sound(key)] = animal(value)
:
animals=( ["moo"]="cow" ["woof"]="dog")
Veya birleştirin:
declare -A animals=( ["moo"]="cow" ["woof"]="dog")
Sonra onları normal diziler gibi kullanın. kullanım
animals['key']='value'
değer ayarlamak
"${animals[@]}"
değerleri genişletmek
"${!animals[@]}"
(fark !
tuşları genişletmek için)
Onlara teklif vermeyi unutmayın:
echo "${animals[moo]}"
for sound in "${!animals[@]}"; do echo "$sound - ${animals[$sound]}"; done
Bash 4'ten önce ilişkilendirilebilir dizileriniz yok. Onları taklit etmek için kullanmayıneval
. Önlemek eval
çünkü, veba gibi olan kabuk komut dosyası vebası. Bunun en önemli nedeni, eval
verilerinize yürütülebilir kod olarak davranılmasıdır (başka birçok nedeni de vardır).
İlk ve en önemlisi : Bash 4'e geçmeyi düşünün. Bu, tüm süreci sizin için daha kolay hale getirecektir.
Yükseltme yapamamanızın bir nedeni varsa, declare
çok daha güvenli bir seçenektir. Verileri bash kodu gibi değerlendirmez eval
ve bu nedenle rastgele kod enjeksiyonuna oldukça kolay izin vermez.
Cevabı kavramları tanıtarak hazırlayalım:
İlk olarak, dolaylı.
$ animals_moo=cow; sound=moo; i="animals_$sound"; echo "${!i}"
cow
İkinci olarak, declare
:
$ sound=moo; animal=cow; declare "animals_$sound=$animal"; echo "$animals_moo"
cow
Onları bir araya getirin:
# Set a value:
declare "array_$index=$value"
# Get a value:
arrayGet() {
local array=$1 index=$2
local i="${array}_$index"
printf '%s' "${!i}"
}
Kullanalım:
$ sound=moo
$ animal=cow
$ declare "animals_$sound=$animal"
$ arrayGet animals "$sound"
cow
Not: declare
bir işleve yerleştirilemez. declare
Bir bash işlevinin içindeki herhangi bir kullanım, yerel olarak oluşturduğu değişkeni bu işlevin kapsamına çevirir , yani global dizilere onunla erişemeyiz veya değiştiremeyiz. (Bash 4'te global değişkenleri bildirmek için declare -g komutunu kullanabilirsiniz - ancak bash 4'te bu çözümden kaçınarak ilk etapta ilişkisel diziler kullanabilirsiniz.)
Özet:
declare -A
ilişkilendirilebilir diziler için kullanın .declare
Yükseltme yapamıyorsanız seçeneği kullanın .awk
Bunun yerine kullanmayı düşünün ve sorunu tamamen önleyin.4.x
ve vermemelidir y
.
sudo port install bash
(akıllıca, IMHO), işlem başına ayrıcalık yükselmesi olmadan tüm kullanıcılar için PATH dizinlerini dizin yapmak istemeyenlere.
Parametre ikamesi vardır, ancak PC de olabilir ... dolaylı gibi.
#!/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]##*:}"
BASH 4 yolu elbette daha iyi, ama bir saldırıya ihtiyacınız varsa ... sadece bir saldırı yapacak. Diziyi / karmayı benzer tekniklerle arayabilirsiniz.
VALUE=${animal#*:}
Davayı korumak için bunu değiştiririmARRAY[$x]="caesar:come:see:conquer"
for animal in "${ARRAY[@]}"; do
Burada aradığım şey:
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
Bu benim için bash 4.1.5 ile işe yaramadı:
animals=( ["moo"]="cow" )
Hput () / hget () arabirimini, hash'leri aşağıdaki gibi adlandırdığınız şekilde değiştirebilirsiniz:
hput() {
eval "$1""$2"='$3'
}
hget() {
eval echo '${'"$1$2"'#hash}'
}
ve sonra
hput capitals France Paris
hput capitals Netherlands Amsterdam
hput capitals Spain Madrid
echo `hget capitals France` and `hget capitals Netherlands` and `hget capitals Spain`
Bu, çakışmayan diğer haritaları tanımlamanıza olanak tanır (örneğin, başkent tarafından ülke araması yapan 'rcapitals'). Ama her iki durumda da, bunun oldukça korkunç, performans açısından olduğunu göreceksiniz.
Gerçekten hızlı karma arama istiyorsanız, gerçekten iyi çalışan korkunç, korkunç bir kesmek var. Bu: anahtarınızı / değerlerinizi geçici bir dosyaya, her satıra bir tane yazın, sonra bunları kesmek için 'grep "^ $ key"' kullanın, kesme veya awk veya sed ile boruları veya değerleri almak için ne olursa olsun.
Dediğim gibi, kulağa korkunç geliyor ve yavaş olması ve her türlü gereksiz IO yapması gerektiği gibi geliyor, ama pratikte çok hızlı (disk önbelleği harika, değil mi?) tablolar. Anahtar benzersizliğini kendiniz, vb. Zorlamak zorundasınız. Sadece birkaç yüz girişiniz olsa bile, çıktı dosyası / grep combo biraz daha hızlı olacak - deneyimlerime göre birkaç kez daha hızlı. Ayrıca daha az bellek yer.
İşte bunu yapmanın bir yolu:
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`
Dosya sistemi, karma harita olarak kullanılabilen bir ağaç yapısıdır. Karma tablonuz geçici bir dizin, anahtarlarınız dosya adları ve değerleriniz dosya içeriği olacaktır. Avantajı, büyük hashetleri işleyebilmesidir ve belirli bir kabuk gerektirmez.
hashtable=$(mktemp -d)
echo $value > $hashtable/$key
value=$(< $hashtable/$key)
Tabii ki, yavaş, ama o kadar yavaş değil . Makinemde bir SSD ve btrfs ile test ettim ve saniyede yaklaşık 3000 eleman okuma / yazma yapıyor .
mkdir -d
? (4.3 değil, Ubuntu 14'te. Ben mkdir /run/shm/foo
mkdir /tmp/foo
mktemp -d
bunun yerine kastedildi?
$value=$(< $hashtable/$key)
ve arasındaki fark value=$(< $hashtable/$key)
nedir? Teşekkürler!
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
${var#start}
, var değişkeninde saklanan değerin başından itibaren metnin başlangıcını kaldırır .
Aşağıdaki ufw güvenlik duvarı komut dosyasındaki kod snippet'inde gösterildiği gibi bash yerleşik oku kullanarak bir çözüm düşünün . Bu yaklaşımın avantajı, istendiği kadar sınırlandırılmış alan kümesi (sadece 2 değil) kullanma avantajına sahiptir. Kullandık | çünkü sınır aralığı belirteçleri iki nokta üst üste işaret gerektirebilir, yani 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
IFS=$'|' read -r first rest <<< "$fields"
@Lhunath ve diğerleri ile ilişkisel dizinin Bash 4 ile gitmenin yolu olduğunu kabul ediyorum. Bash 3'e (OSX, güncelleyemediğiniz eski dağıtımlar) sıkışmışsanız, her yerde olması gereken bir ifade de kullanabilirsiniz. ve düzenli ifadeler. Özellikle sözlük çok büyük olmadığında hoşuma gidiyor.
Haritanızı dize olarak yazın (başında ve sonunda da ',' ayırıcısına dikkat edin)
animals=",moo:cow,woof:dog,"
Değerleri ayıklamak için normal ifade kullanın
get_animal {
echo "$(expr "$animals" : ".*,$1:\([^,]*\),.*")"
}
Öğeleri listelemek için dizeyi bölün
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
}
Şimdi kullanabilirsiniz:
$ animal = get_animal "moo"
cow
$ get_animal_items
cow likes to moo
dog likes to woof
Al P'nin cevabını gerçekten çok beğendim ama benzersizliğin ucuza uygulanmasını istedim, bu yüzden bir adım daha attım - bir dizin kullanın. Bazı belirgin sınırlamalar (dizin dosyası sınırları, geçersiz dosya adları) vardır, ancak çoğu durumda işe yaramalıdır.
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
Ayrıca testlerimde biraz daha iyi performans gösteriyor.
$ 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
Sadece içeri gireceğimi düşündüm. Şerefe!
Düzenle: hdestroy () ekleme
İki şey, / dev / shm (Redhat) kullanarak herhangi bir çekirdek 2.6 / tmp yerine bellek kullanabilirsiniz diğer dağıtımlar değişebilir. Ayrıca hget aşağıdaki gibi okunarak yeniden uygulanabilir:
function hget {
while read key idx
do
if [ $key = $2 ]
then
echo $idx
return
fi
done < /dev/shm/hashmap.$1
}
Buna ek olarak, tüm tuşların benzersiz olduğunu varsayarak, dönüş kısa devre okuma döngüsünü devreder ve tüm girişleri okumak zorunda kalmaz. Uygulamanızda yinelenen anahtarlar varsa, dönüşü dışarıda bırakmanız yeterlidir. Bu, hem grep hem de awk'yi okuma ve çatallama masrafından tasarruf sağlar. Her iki uygulama için / dev / shm kullanılması, son girişi arayan 3 girişlik karede time hget kullanarak aşağıdakileri verdi:
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
Okuma / echo:
$ time echo $(hget FD oracle)
3
real 0m0.004s
user 0m0.000s
sys 0m0.004s
Birden fazla çağrıda% 50'den daha az iyileşme görmedim. Tüm bunlar, kullanımı nedeniyle başın üzerindeki çatala atfedilebilir /dev/shm
.
Bir iş arkadaşı bu konudan bahsetti. Bağımsız olarak karma tabloları bash içinde uyguladım ve sürüm 4'e bağlı değil . Bash'da Hash tabloları başlıklı Mart 2010'da (burada bazı cevaplardan önce ...) bir blog yayınımdan :
Daha öncecksum
karma için kullanılır ama o zamandan beri Java dize hashCode yerel bash / zsh çevirmiş .
# Here's the hashing function
ht() {
local h=0 i
for (( i=0; i < ${#1}; i++ )); do
let "h=( (h<<5) - h ) + $(printf %d \'${1:$i:1})"
let "h |= h"
done
printf "$h"
}
# Example:
myhash[`ht foo bar`]="a value"
myhash[`ht baz baf`]="b value"
echo ${myhash[`ht baz baf`]} # "b value"
echo ${myhash[@]} # "a value b value" though perhaps reversed
echo ${#myhash[@]} # "2" - there are two values (note, zsh doesn't count right)
İki yönlü değildir ve yerleşik yol çok daha iyidir, ancak ikisi de gerçekten kullanılmamalıdır. Bash hızlı bir defalık içindir ve bu tür şeyler, belki de sizin ~/.bashrc
ve arkadaşlarınız hariç, hash gerektirebilecek karmaşıklığı nadiren içermelidir .
Bash 4'ten önce bash'de ilişkisel diziler kullanmanın iyi bir yolu yoktur. En iyi seçeneğiniz, aslında awk gibi şeyleri destekleyen yorumlanmış bir dil kullanmaktır. Öte yandan, bash 4 yapar onlara destek.
Bash 3'te daha az iyi yollara gelince , işte yardımcı olabileceklerden bir referans: http://mywiki.wooledge.org/BashFAQ/006
Bash 3 çözeltisi:
Bazı cevapları okurken, diğerlerine yardımcı olabilecek hızlı ve küçük bir işlevi bir araya getirdim.
# 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[@])
Ayrıca bash4 yolunu kullandım ama can sıkıcı bir hata buluyorum.
Böylece bu şekilde kullanılan dinamik ilişkilendirilebilir dizi içeriği güncelleştirmek gerekiyordu:
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
Bash 4.3.11 ile dikte mevcut bir anahtara eklenmenin zaten mevcutsa değerin eklenmesiyle sonuçlandığını öğrendim. Örneğin, bir miktar tekrardan sonra değerin içeriği "checkKOcheckKOallCheckOK" idi ve bu iyi değildi.
Bash 4.3.39 ile ilgili bir sorun yok, burada mevcut bir anahtarın işaretlenmesi, mevcutsa aktüal değeri azaltmak için anlamına gelir.
Bu sadece temizleme / cicle önce statusCheck ilişkisel dizi bildirerek çözüldü:
unset statusCheck; declare -A statusCheck
Dinamik değişkenleri kullanarak bash 3'te HashMaps oluşturuyorum. Bunun cevabımda nasıl çalıştığını açıkladım: Shell betiklerindeki ilişkilendirilebilir diziler
Ayrıca bash 3'te yapılan bir HashMap uygulaması olan shell_map'e göz atabilirsiniz .