셸 스크립트의 연관 배열


답변:


20

Irfan의 답변에 추가하려면 get()맵 콘텐츠에 대한 반복이 필요하지 않기 때문에 더 짧고 빠른 버전이 있습니다.

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

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

16
서브 쉘과 sed를 포크하는 것은 최적이 아닙니다. Bash4는이를 기본적으로 지원하며 bash3에는 더 나은 대안이 있습니다.
lhunath

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, 또는 그것을 전혀 필요하지 않을 수도있다.


답변을 게시 해 주셔서 감사합니다. bash 4.0 이상을 사용하는 사람들을 위해 최선의 방법이라고 생각합니다.
Irfan Zulfiqar

BASH_VERSION이 설정되어 있는지 확인하기 위해 약간의 kludge를 추가하고> = 4입니다. 그리고 예, BASH 4는 정말, 정말 멋집니다!
Tim Post

나는 이와 같은 것을 사용하고 있습니다. 배열 인덱스 / 구독이 존재하지 않는 오류를 "잡는"가장 좋은 방법은 무엇입니까? 예를 들어 아래 첨자를 명령 줄 옵션으로 사용하고 사용자가 오타를 입력하고 "designatio"를 입력하면 어떻게됩니까? "잘못된 배열 아래 첨자"오류가 발생하지만 가능한 경우 배열 조회시 입력을 검증하는 방법이 없습니까?
Jer

3
@Jer 꽤 모호하지만 셸에 변수가 설정되어 있는지 확인하려면 사용할 수 있습니다 test -z ${variable+x}( x문제가되지 않습니다. 문자열이 될 수 있음). Bash의 연관 배열의 경우 유사하게 수행 할 수 있습니다. 를 사용하십시오 test -z ${map[key]+x}.
Brian Campbell

95

또 다른 비 bash 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"

거기에서 검색하기 위해 if 문을 던질 수도 있습니다. if [[$ var = ~ / blah /]]. 또는 무엇이든.


2
이 방법은 실제로 Bash 4가 없을 때 좋습니다. 하지만 VALUE를 가져 오는 줄은 VALUE = $ {animal # * :}과 같이 더 안전 할 것이라고 생각합니다. 하나의 # 문자 만 있으면 첫 번째 ":"에서 일치가 중지됩니다. 그러면 값에 ":"도 포함될 수 있습니다.
Ced-le-pingouin

@ Ced-le-pingouin ~ 그거 좋은 포인트입니다! 인식하지 못했습니다. 제안 된 개선 사항을 반영하기 위해 내 게시물을 편집했습니다.
Bubnoff 2011

1
BASH 매개 변수 대체를 사용하는 연관 배열의 꽤 끔찍한 에뮬레이션입니다. "key"param-sub 는 콜론 의 모든 것을 대체 하고 value 패턴 은 콜론 의 모든 것을 대체 합니다. 정규식 와일드 카드 일치와 유사합니다. 따라서 진정한 연관 배열이 아닙니다 . BASH 3 이하에서 해시 / 연관 배열과 같은 기능을 수행하는 이해하기 쉬운 방법이 필요한 경우가 아니면 권장하지 않습니다. 그래도 작동합니다! 더보기 : tldp.org/LDP/abs/html/parameter-substitution.html#PSOREX2
Bubnoff

1
키로 항목을 조회하는 방법을 제공하지 않기 때문에 연관 배열을 구현하지 않습니다. 숫자 인덱스에서 각 키 (및 값)를 찾는 방법 만 제공합니다. (아이템은 배열을 반복하여 키를 찾았지만 그 연관 배열을 위해 요구되는 것이 아닙니다 수 있습니다.)
에릭 Postpischil에게

@EricPostpischil 맞아요. 해킹 일뿐입니다. 그것은 사람이 설정에서 익숙한 구문을 사용할 수 있도록 허용하지만 여전히 당신이 말한 것처럼 배열을 반복해야합니다. 나는 이전 의견에서 분명히 연관 배열이 아니라는 것을 분명히하려고 노력했으며 대안이 있으면 권장하지도 않습니다. 내 관점에서 볼 때 유일하게 유리한 점은 Python과 같은 다른 언어에 익숙한 사람들이 작성하고 사용하기 쉽다는 것입니다. 실제로 BASH 3에서 연관 배열을 구현하려는 시점에 있다면 단계를 약간 되돌려 야 할 수도 있습니다.
Bubnoff

34

뒤로 물러서서 맵 또는 연관 배열이 실제로 무엇인지 생각해야한다고 생각합니다. 주어진 키에 대한 값을 저장하고 해당 값을 빠르고 효율적으로 되 돌리는 방법뿐입니다. 또한 키를 반복하여 모든 키 값 쌍을 검색하거나 키와 관련 값을 삭제할 수 있습니다.

이제 쉘 스크립팅에서 항상 사용하는 데이터 구조에 대해 생각해보십시오. 스크립트를 작성하지 않고 쉘에서도 이러한 속성을 가지고 있습니다. 당황? 파일 시스템입니다.

실제로 쉘 프로그래밍에서 연관 배열을 갖는 데 필요한 것은 임시 디렉토리뿐입니다. mktemp -d연관 배열 생성자입니다.

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

echoand를 사용 하고 싶지 않다면 언제든지 cat작은 래퍼를 작성할 수 있습니다. 이러한 것들은 Irfan의 모델로 모델링되었지만 $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 :이 접근 방식은 실제로 질문자가 제안한 sed를 사용하는 선형 검색보다 훨씬 빠르며 더 강력합니다 (키와 값에-, =, 공백, qnd ": SP :"를 포함 할 수 있음). 파일 시스템을 사용한다고해서 속도가 느려지지는 않습니다. 이러한 파일은 호출하지 않는 한 실제로 디스크에 기록된다는 보장이 없습니다 sync. 수명이 짧은 이와 같은 임시 파일의 경우 많은 파일이 디스크에 기록되지 않을 가능성이 적습니다.

다음 드라이버 프로그램을 사용하여 Irfan의 코드, Jerry의 Irfan 코드 수정 및 내 코드에 대한 몇 가지 벤치 마크를 수행했습니다.

#!/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 irfan 10 5

    실제 0m0.975s
    사용자 0m0.280s
    시스템 0m0.691s

    $ 시간 ./driver.sh brian 10 5

    실제 0m0.226s
    사용자 0m0.057s
    시스템 0m0.123s

    $ 시간 ./driver.sh jerry 10 5

    실제 0m0.706s
    사용자 0m0.228s
    시스템 0m0.530s

    $ 시간 ./driver.sh irfan 100 5

    실제 0m10.633s
    사용자 0m4.366s
    sys 0m7.127s

    $ 시간 ./driver.sh brian 100 5

    진짜 0m1.682s
    사용자 0m0.546s
    시스템 0m1.082s

    $ 시간 ./driver.sh jerry 100 5

    실제 0m9.315s
    사용자 0m4.565s
    시스템 0m5.446s

    $ 시간 ./driver.sh irfan 10500

    실제 1m46.197s
    사용자 0m44.869s
    시스템 1m12.282s

    $ 시간 ./driver.sh brian 10500

    실제 0m16.003s
    사용자 0m5.135s
    시스템 0m10.396s

    $ 시간 ./driver.sh jerry 10500

    진짜 1m24.414s
    사용자 0m39.696s
    sys 0m54.834s

    $ 시간 ./driver.sh irfan 1000 5

    진짜 4m25.145s
    사용자 3m17.286s
    시스템 1 분 21.490 초

    $ 시간 ./driver.sh brian 1000 5

    진짜 0m19.442s
    사용자 0m5.287s
    sys 0m10.751s

    $ 시간 ./driver.sh jerry 1000 5

    진짜 5m29.136s
    사용자 4m48.926s
    sys 0m59.336s


1
기본적으로 메모리에서 상당히 빠르게 수행 할 수있는 작업을 위해 IO를 사용하는 맵에 파일 시스템을 사용해야한다고 생각하지 않습니다.
Irfan Zulfiqar

9
파일이 반드시 디스크에 기록되는 것은 아닙니다. sync를 호출하지 않으면 운영 체제가 메모리에 남겨 둘 수 있습니다. 귀하의 코드는 sed를 호출하고 여러 선형 검색을 수행하며 모두 매우 느립니다. 몇 가지 빠른 벤치 마크를 수행했는데 내 버전이 5 ~ 35 배 더 빠릅니다.
Brian Campbell

반면에 bash4의 기본 배열이 훨씬 더 나은 접근 방식이며 bash3에서는 선언 및 간접 지정을 사용하여 분기하지 않고도 디스크에서 모든 것을 유지할 수 있습니다.
lhunath

7
"빠른"과 "쉘"은 어차피 실제로 함께 사용되지 않습니다. "미소 IO 방지"수준에서 말하는 속도 문제에 대해서는 확실히 아닙니다. IO가 없음을 보장하기 위해 / dev / shm을 검색하고 사용할 수 있습니다.
jmtd 2011

2
이 솔루션은 저를 놀라게했고 굉장합니다. 2016 년에도 여전히 사실입니다. 정말 받아 들여진 대답이어야합니다.
Gordon

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 : Own : SP : Company로 저장된 끝없는 문자열 일뿐입니다.

값의 경우 공백은 ': 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그것이 bash 코드 인 것처럼 데이터를 보내고 있으며, 더 많은 것 : 당신은 그것을 적절하게 인용하지 못한다. 둘 다 많은 버그와 임의의 코드 삽입을 유발합니다.
lhunath

3

Bash 3의 경우 멋지고 간단한 솔루션이있는 특별한 경우가 있습니다.

당신은 많은 변수를 처리하지 않으려는 경우, 또는 키를 단순히 잘못된 변수 식별자, 당신의 배열이 보장 256 개 항목을 당신이 함수의 반환 값을 악용 할 수 있습니다. 이 솔루션은 값을 변수로 쉽게 사용할 수 있기 때문에 서브 쉘이 필요하지 않으며 성능이 비명을 지르는 반복도 필요하지 않습니다. 또한 Bash 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그렇지 않으면 globbing의 대상이됩니다. 처음부터 정적 / 고정 해시에 매우 유용하지만 hash_keys=()배열 에서 인덱스 생성기를 작성할 수 있습니다.

주의하세요. 기본값은 첫 번째 요소이므로 0 번째 요소를 따로 설정하는 것이 좋습니다.

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

주의 : 이제 길이가 잘못되었습니다.

또는 0부터 시작하는 인덱싱을 유지하려는 경우 다른 인덱스 값을 예약하고 존재하지 않는 키에 대해 보호 할 수 있지만 읽기가 어렵습니다.

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

또는 길이를 올바르게 유지하려면 인덱스를 1만큼 오프셋하십시오.

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

다음 명령은 map _ $ {person} 형식으로 동적 변수를 키로 사용하여 모든 것을 합산합니다 .

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 Object , shell_map 처럼 작동하는 함수를 개발 입니다.

" HashMap 인스턴스 " 를 생성하기 위해 shell_map 함수 는 다른 이름으로 자신의 복사본을 생성 할 수 있습니다. 각각의 새 함수 사본에는 다른 $ FUNCNAME 변수가 있습니다. 그런 다음 $ FUNCNAME을 사용하여 각 Map 인스턴스에 대한 네임 스페이스를 만듭니다.

맵 키는 $ FUNCNAME_DATA_ $ KEY 형식의 전역 변수입니다. 여기서 $ 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

또 다른 비 bash-4 (즉, bash 3, 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

인쇄물:

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

몇 년 전에 다른 기능 (로깅, 구성 파일, 명령 줄 인수에 대한 확장 지원, 도움말 생성, 단위 테스트 등) 중에서 연관 배열을 지원하는 bash 용 스크립트 라이브러리를 작성했습니다. 라이브러리에는 연관 배열 용 래퍼가 포함되어 있으며 자동으로 적절한 모델로 전환됩니다 (bash4의 경우 내부, 이전 버전의 경우 에뮬레이션). 쉘 프레임 워크라고 불리며 origo.ethz.ch에서 호스팅되었지만 오늘은 리소스가 닫혀 있습니다. 누군가가 여전히 필요하다면 공유 할 수 있습니다.


GitHub의에 그것을 고집 가치가있을 수도 있습니다
마크 K 코완

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

Vadim의 솔루션을 다음과 같이 수정했습니다.

####################################################################
# 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 방화벽 스크립트의 코드 스 니펫에 설명 된대로 bash 내장 읽기 를 사용하여 이러한 방식으로 문제를 해결하는 것을 고려하십시오 . 이 접근 방식은 원하는만큼의 구분 된 필드 집합 (단지 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.