태그 보관물: associative-array

associative-array

셸 스크립트의 연관 배열 스크립팅을 위해

쉘 스크립팅을 위해 연관 배열 또는 맵과 같은 데이터 구조를 시뮬레이션하는 스크립트가 필요했습니다.



답변

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

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

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


답변

이식성이 주요 관심사가 아닌 경우 또 다른 옵션은 셸에 내장 된 연관 배열을 사용하는 것입니다. 이것은 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 방법.

#!/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 /]]. 또는 무엇이든.


답변

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

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

실제로 쉘 프로그래밍에서 연관 배열을 갖는 데 필요한 것은 임시 디렉토리뿐입니다. 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


답변

Bash4는이를 기본적으로 지원합니다. 사용하지 마십시오 grep또는 eval, 그들은 해킹의 추악한입니다.

예제 코드가 포함 된 자세한 답변은 /programming/3467959를 참조하십시오.


답변

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


답변

이제이 질문에 대답합니다.

다음 스크립트는 셸 스크립트에서 연관 배열을 시뮬레이션합니다. 간단하고 이해하기 쉽습니다.

맵은 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"
          `
}