Kabuk betiklerinde ilişkilendirilebilir diziler


Yanıtlar:


20

İrfan'ın cevabına eklemek için get(), harita içeriği üzerinde yineleme gerektirmediğinden , burada daha kısa ve daha hızlı bir versiyonu var :

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

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

16
bir alt kabuğu ve sed'i çatallamak pek optimal değildir. Bash4 bunu yerel olarak destekler ve bash3'ün daha iyi alternatifleri vardır.
lhunath

149

Diğer bir seçenek, taşınabilirlik ana endişeniz değilse, kabukta yerleşik olan ilişkilendirilebilir dizileri kullanmaktır. Bu, bash 4.0'da çalışmalıdır (şimdi çoğu büyük dağıtımda mevcuttur, ancak OS X'te kendiniz yüklemediğiniz sürece değil), ksh ve zsh:

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

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

Kabuğa bağlı olarak, typeset -A newmapyerine bir tane yapmanız gerekebilir declare -A newmapveya bazılarında hiç gerekli olmayabilir.


Cevabınızı gönderdiğiniz için teşekkürler, bence bunu bash 4.0 veya üstü kullanan erkekler için yapmanın en iyi yolu budur.
Irfan Zulfiqar

BASH_VERSION'ın ayarlandığından ve> = 4 olduğundan emin olmak için biraz kludge eklerim. Ve evet, BASH 4 gerçekten çok harika!
Tim Post

Bunun gibi bir şey kullanıyorum. Dizi indeksinin / alt indisinin olmadığı bir hatayı "yakalamanın" en iyi yolu nedir? Örneğin, alt simgeyi bir komut satırı seçeneği olarak alıyorsam ve kullanıcı bir yazım hatası yapıp "designatio" girdiyse? "Hatalı dizi alt simge" hatası alıyorum, ancak mümkünse, dizi araması sırasında girdiyi nasıl doğrulamam?
Jer

3
@Jer Oldukça belirsiz, ancak kabukta bir değişken ayarlanıp ayarlanmadığını belirlemek için kullanabilirsiniz test -z ${variable+x}( xönemli değil, bu herhangi bir dize olabilir). Bash'deki ilişkilendirilebilir bir dizi için, benzerini yapabilirsiniz; kullanın test -z ${map[key]+x}.
Brian Campbell

95

Bash olmayan başka bir 4 yol.

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

Orada arama yapmak için bir if ifadesi de atabilirsiniz. eğer [[$ var = ~ / blah /]]. ya da her neyse.


2
Bu yöntem, Bash 4'e sahip olmadığınız zaman iyidir. Ancak DEĞER'i getiren satırın bu şekilde daha güvenli olacağını düşünüyorum: VALUE = $ {hayvan # *:}. Yalnızca bir # karakterle, eşleştirme ilk ":" üzerinde durur. Bu, değerlerin ":" içermesine de izin verir.
Ced-le-pingouin

@ Ced-le-pingouin ~ Bu harika bir nokta! Ben anlayamadım. Gönderimi, önerdiğiniz iyileştirmeleri yansıtacak şekilde düzenledim.
Bubnoff

1
BASH parametre ikamesi kullanan ilişkisel dizilerin oldukça hilekâr bir öykünmesi. "Anahtar" param-alt , iki nokta üst üste işaretinden önceki her şeyin yerine geçer ve değer modeli iki nokta üst üste işaretinden sonraki her şeyin yerine geçer . Normal ifade joker karakter eşleşmesine benzer. Yani gerçek bir ilişkisel dizi DEĞİL . BASH 3 veya altında hash / ilişkisel dizi benzeri işlevsellik yapmanın kolay anlaşılır bir yoluna ihtiyacınız olmadığı sürece önerilmez. Yine de çalışıyor! Daha fazla bilgi için: tldp.org/LDP/abs/html/parameter-substitution.html#PSOREX2
Bubnoff

1
Bu, bir öğeyi anahtara göre aramak için bir yol sağlamadığından ilişkilendirilebilir bir dizi uygulamaz. Yalnızca her bir anahtarı (ve değeri) sayısal bir dizinden bulmanın bir yolunu sağlar. (Bir öğe, dizi boyunca yinelenerek anahtarla bulunabilir, ancak bu bir ilişkisel dizi için istenen şey değildir.)
Eric Postpischil

@EricPostpischil Doğru. Bu sadece bir hack. Bir kişinin kurulumda tanıdık sözdizimini kullanmasına izin verir, ancak yine de söylediğiniz gibi dizi boyunca yineleme gerektirir. Önceki yorumumda bunun kesinlikle ilişkilendirilebilir bir dizi olmadığını netleştirmeye çalıştım ve alternatifleriniz varsa bunu bile önermiyorum. Benim görüşüme göre lehine tek nokta, Python gibi diğer dillere aşina olanlar için yazmanın ve kullanmanın kolay olmasıdır. BASH 3'te ilişkilendirilebilir dizileri gerçekten uygulamak istediğiniz bir noktadaysanız, adımlarınızı biraz yeniden izlemeniz gerekebilir.
Bubnoff

34

Sanırım geri adım atıp haritanın veya ilişkisel dizinin gerçekte ne olduğunu düşünmeniz gerekiyor. Tüm bunlar, belirli bir anahtar için bir değeri depolamanın ve bu değeri hızlı ve verimli bir şekilde geri almanın bir yoludur. Ayrıca, her bir anahtar-değer çiftini almak veya anahtarları ve bunların ilişkili değerlerini silmek için anahtarlar üzerinde yineleme yapabilmek isteyebilirsiniz.

Şimdi, kabuk komut dosyası oluşturmada her zaman kullandığınız ve hatta bir komut dosyası yazmadan yalnızca kabukta bu özelliklere sahip olan bir veri yapısını düşünün. Kafası karışmış? Dosya sistemi.

Gerçekten, kabuk programlamada ilişkilendirilebilir bir diziye sahip olmak için ihtiyacınız olan tek şey bir geçici dizindir. mktemp -dilişkisel dizi kurucunuz:

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

Kullandığınız gibi hissediyorum yoksa echove cat, her zaman bazı küçük sarmalayıcılarını yazabilirsiniz; bunlar, İran'ınkinden modellenmiştir, ancak aşağıdakiler gibi keyfi değişkenler ayarlamak yerine sadece değeri çıkarırlar $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

edit : Bu yaklaşım, sorgulayıcı tarafından önerilen sed kullanan doğrusal aramadan biraz daha hızlıdır ve daha sağlamdır (anahtarların ve değerlerin -, =, boşluk, qnd ": SP:" içermesine izin verir). Dosya sistemini kullanıyor olması onu yavaşlatmaz; bu dosyaların aslında siz çağırmadığınız sürece diske yazılacağı asla garanti edilmez sync; Bu gibi kısa ömürlü geçici dosyalar için, çoğunun asla diske yazılmaması olası değildir.

Aşağıdaki sürücü programını kullanarak, İran'ın kodunun, Jerry'nin İran'ın kodunun ve kodumun birkaç karşılaştırmasını yaptım:

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

Sonuçlar:

    $ time ./driver.sh irfan 10 5

    gerçek 0m0.975s
    kullanıcı 0m0.280s
    sys 0d0.691sn

    $ time ./driver.sh brian 10 5

    gerçek 0a0.226s
    kullanıcı 0m0.057s
    sys 0d0,123sn

    $ zaman ./driver.sh jerry 10 5

    gerçek 0a0.706s
    kullanıcı 0m0.228s
    sys 0d0.530sn

    $ time ./driver.sh irfan 100 5

    gerçek 0m10.633s
    kullanıcı 0m4.366s
    sys 0d7.127s

    $ time ./driver.sh brian 100 5

    gerçek 0m1.682s
    kullanıcı 0m0.546sn
    sys 0d1.082sn

    $ time ./driver.sh jerry 100 5

    gerçek 0m9.315s
    kullanıcı 0m4.565s
    sys 0d5.446sn

    $ time ./driver.sh irfan 10500

    gerçek 1m46.197s
    kullanıcı 0m44.869s
    sys 1d12.282s

    $ zaman ./driver.sh brian 10500

    gerçek 0a16.003sn
    kullanıcı 0d5.135s
    sys 0d10.396s

    $ zaman ./driver.sh jerry 10500

    gerçek 1a24.414s
    kullanıcı 0m39.696s
    sys 0d54.834sn

    $ time ./driver.sh irfan 1000 5

    gerçek 4a25.145s
    kullanıcı 3m17.286s
    sys 1a21,490sn

    $ time ./driver.sh brian 1000 5

    gerçek 0a19.442sn
    kullanıcı 0d5.287s
    sys 0d10.751s

    $ zaman ./driver.sh jerry 1000 5

    gerçek 5m29.136s
    kullanıcı 4d48.926s
    sys 0d59.336sn


1
Haritalar için dosya sistemi kullanmanız gerektiğini düşünmüyorum, temelde IO'yu bellekte oldukça hızlı yapabileceğiniz bir şey için kullanıyor.
İrfan Zülfikar

9
Dosyalar mutlaka diske yazılmayacaktır; senkronizasyonu çağırmadığınız sürece, işletim sistemi bunları bellekte bırakabilir. Kodunuz sed'e sesleniyor ve hepsi çok yavaş olan birkaç doğrusal arama yapıyor. Bazı hızlı testler yaptım ve sürümüm 5-35 kat daha hızlı.
Brian Campbell

Öte yandan, bash4'ün yerel dizileri önemli ölçüde daha iyi bir yaklaşımdır ve bash3'te, bildirme ve indirme kullanarak her şeyi çatallamadan diskten uzak tutabilirsiniz.
lhunath

7
"hızlı" ve "kabuk" gerçekten bir arada bulunmaz: kesinlikle "minik IO'dan kaçınma" düzeyinde bahsettiğimiz türden hız sorunları için değil. IO olmamasını garanti etmek için / dev / shm öğesini arayabilir ve kullanabilirsiniz.
jmtd

2
Bu çözüm beni şaşırttı ve harika. 2016'da hala geçerli. Gerçekten kabul edilen cevap bu olmalı.
Gordon

7

Bash4 bunu yerel olarak destekler. Kullanmayın grepya da eval, bunlar en çirkin hackler.

Örnek kodla ayrıntılı, ayrıntılı bir yanıt için bkz .: /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; }'
}

Misal:

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

Şimdi bu soruyu cevaplıyorum.

Aşağıdaki komut dosyaları, kabuk komut dosyalarındaki ilişkili dizileri simüle eder. Basit ve anlaşılması çok kolay.

Harita, keyValuePair'in --name = Irfan --designation = SSE --company = My: SP: Own: SP: Company olarak kaydedildiği hiç bitmeyen bir dizeden başka bir şey değildir

değerler için boşluklar ': SP:' ile değiştirilir

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

düzenleme: Tüm anahtarları almak için başka bir yöntem eklendi.

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

    mapName=$1; 

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

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

1
evalVerileri bash kodu gibi alıyorsunuz ve dahası: doğru şekilde alıntı yapmıyorsunuz . Her ikisi de çok sayıda hataya ve keyfi kod enjeksiyonuna neden olur.
lhunath

3

Bash 3 için güzel ve basit bir çözüme sahip özel bir durum var:

Çok fazla değişkeni işlemek istemiyorsanız veya anahtarlar sadece geçersiz değişken tanımlayıcılarıysa ve dizinizin 256 öğeden az olması garantiliyse , işlev dönüş değerlerini kötüye kullanabilirsiniz. Bu çözüm, değer bir değişken olarak hazır olduğundan herhangi bir alt kabuk veya performansın çığlık atması için herhangi bir yineleme gerektirmez. Ayrıca neredeyse Bash 4 sürümü gibi çok okunabilir.

İşte en temel versiyon:

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

Unutmayın, içinde tek tırnak kullanın case, aksi takdirde genellemeye tabidir. Başlangıçtan itibaren statik / donmuş karmalar için gerçekten kullanışlıdır, ancak bir hash_keys=()diziden bir dizin oluşturucu yazılabilir .

Dikkat edin, varsayılan olarak birinciye ayarlıdır, bu nedenle sıfırıncı öğeyi bir kenara bırakmak isteyebilirsiniz:

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

Uyarı: uzunluk artık yanlış.

Alternatif olarak, sıfır tabanlı indekslemeyi korumak istiyorsanız, başka bir indeks değeri ayırabilir ve var olmayan bir anahtara karşı koruma sağlayabilirsiniz, ancak daha az okunabilir:

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

Veya uzunluğu doğru tutmak için dizini birer birer kaydırın:

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

Dinamik değişken adlarını kullanabilir ve değişken adlarının bir karma haritanın anahtarları gibi çalışmasına izin verebilirsiniz.

Örneğin, aşağıdaki örnekte olduğu gibi, iki sütunlu, ad, kredi içeren bir girdi dosyanız varsa ve her kullanıcının gelirini toplamak istiyorsanız:

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

Aşağıdaki komut, dinamik değişkenleri anahtar olarak kullanarak, _ $ {person} haritası biçiminde her şeyi toplar :

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

Sonuçları okumak için:

set | grep map

Çıktı şu şekilde olacaktır:

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

Bu teknikleri detaylandırırken, GitHub'da bir HashMap Nesnesi gibi çalışan bir işlev , shell_map geliştiriyorum .

" HashMap örnekleri " oluşturmak için , shell_map işlevi farklı isimler altında kendi kopyalarını oluşturabilir. Her yeni işlev kopyasının farklı bir $ FUNCNAME değişkeni olacaktır. $ FUNCNAME daha sonra her bir Harita örneği için bir ad alanı oluşturmak için kullanılır.

Harita anahtarları $ FUNCNAME_DATA_ $ KEY biçimindeki global değişkenlerdir, burada $ KEY Haritaya eklenen anahtardır. Bu değişkenler dinamik değişkenlerdir .

Aşağıda örnek olarak kullanabilmeniz için basitleştirilmiş bir versiyonunu koyacağım.

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

Kullanımı:

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

Yine başka bir bash-4 olmayan (yani bash 3, Mac uyumlu) yol:

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

Baskılar:

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

Fonksiyonlu casebir ilişkisel dizi gibi davranır. Maalesef kullanamaz return, bu yüzden echoçıktısına sahiptir, ancak alt kabukları çatallayan bir pürist değilseniz bu bir problem değildir.


1

Ne yazık ki daha önce soruyu görmemiştim - diğerlerinin yanı sıra haritaları (İlişkilendirilebilir diziler) içeren kütüphane kabuğu çerçevesi yazdım . Son versiyonu burada bulunabilir .

Misal:

#!/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 mevcutsa, başka bir seçenek eklemek:

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

Daha önce de belirtildiği gibi, en iyi performans gösteren yöntemin bir dosyaya anahtar / vals yazmak ve sonra bunları almak için grep / awk kullanmak olduğunu doğru buldum. Her türlü gereksiz IO gibi görünüyor, ancak disk önbelleği devreye giriyor ve onu son derece verimli hale getiriyor - yukarıdaki yöntemlerden birini kullanarak bunları bellekte saklamaya çalışmaktan çok daha hızlı (kıyaslamaların gösterdiği gibi).

İşte sevdiğim hızlı ve temiz bir yöntem:

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`

Anahtar başına tek değeri zorlamak istiyorsanız, hput () 'da küçük bir grep / sed eylemi de yapabilirsiniz.


0

birkaç yıl önce bash için, diğer özellikler arasında (günlük kaydı, yapılandırma dosyaları, komut satırı argümanı için genişletilmiş destek, yardım oluşturma, birim testi vb.) destekleyen komut dosyası kitaplığı yazdım. Kitaplık, ilişkili diziler için bir sarmalayıcı içerir ve otomatik olarak uygun modele geçer (bash4 için dahili ve önceki sürümler için öykünme). Kabuk çerçevesi olarak adlandırıldı ve origo.ethz.ch adresinde barındırıldı, ancak bugün kaynak kapalı. Birinin hala ihtiyacı varsa, sizinle paylaşabilirim.


Github'a yapıştırmaya değer olabilir
Mark K Cowan

0

Kabuğun veri yapısı gibi yerleşik bir haritası yok, öğeleri şu şekilde açıklamak için ham dizeyi kullanıyorum:

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

öğeleri ve özniteliklerini çıkarırken:

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

Bu, diğer insanların cevabından daha akıllıca görünmüyor, ancak yeni insanlar için anlaşılması kolay.


-1

Vadim'in çözümünü aşağıdakilerle değiştirdim:

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

Değişiklik, mevcut olmayan bir anahtar talep ederseniz hataları döndürmesini önlemek için map_get yapmaktır, ancak yan etkisi, eksik haritaları sessizce yok saymasıdır, ancak benim kullanım durumuma daha uygun olmuştur. bir döngüdeki öğeleri atlamak için bir anahtarı kontrol etmek istedi.


-1

Geç yanıt, ancak aşağıdaki ufw güvenlik duvarı betiğinden kod parçacığı içinde gösterildiği gibi okunan bash yerleşikini kullanarak sorunu bu şekilde ele almayı düşünün . Bu yaklaşım, istendiği kadar sınırlandırılmış alan kümesi (sadece 2 değil) kullanma avantajına sahiptir. Kullandık | sınırlayıcı çünkü bağlantı noktası aralığı belirleyicileri iki nokta üst üste gerektirebilir, ör. 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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.