[bash] Bash에서 해시 테이블을 정의하는 방법은 무엇입니까?

파이썬 사전 과 동등 하지만 Bash 는 무엇입니까 (OS X 및 Linux에서 작동해야 함).



답변

배쉬 4

Bash 4는 기본적으로이 기능을 지원합니다. 확인 스크립트의 hashbang이다 #!/usr/bin/env bash또는 #!/bin/bash당신이 사용하게하지 않도록 sh. 당신이 직접 스크립트를 실행하고 있는지 확인하거나 실행 script으로 bash script. (실제로 배쉬와 배쉬 스크립트가 실행되지 않는 일이 될 것입니다 정말 혼란!)

다음을 수행하여 연관 배열을 선언하십시오.

declare -A animals

일반 배열 할당 연산자를 사용하여 요소로 채울 수 있습니다. 예를 들어,의지도를하려는 경우 animal[sound(key)] = animal(value):

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

또는 그것들을 병합하십시오 :

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

그런 다음 일반 배열처럼 사용하십시오. 사용하다

  • animals['key']='value' 값을 설정

  • "${animals[@]}" 값을 확장

  • "${!animals[@]}"!키를 확장하려면 (알림 )

그들을 인용하는 것을 잊지 마십시오 :

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

배쉬 3

bash 4 이전에는 연관 배열이 없습니다. 그것들을 모방하기 위해 사용하지 마십시오eval . 피 eval가 있기 때문에, 전염병처럼 입니다 쉘 스크립트의 전염병. 가장 중요한 이유는 eval데이터를 실행 가능한 코드로 취급하기 때문입니다 (다른 많은 이유도 있음).

가장 먼저 : bash 4로 업그레이드하는 것이 좋습니다. 이렇게하면 전체 프로세스가 훨씬 쉬워집니다.

업그레이드 할 수없는 이유 declare가 있다면 훨씬 안전한 옵션입니다. 데이터를 bash 코드로 평가 eval하지 않으므로 임의 코드 삽입을 매우 쉽게 허용하지 않습니다.

개념을 소개하여 답변을 준비합시다.

먼저 간접적입니다.

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

둘째, declare:

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

그것들을한데 모으십시오 :

# Set a value:
declare "array_$index=$value"

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

사용합시다 :

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

참고 : declare기능에 넣을 수 없습니다. declarebash 함수 내부를 사용 하면 해당 함수의 범위에 로컬 변수를 생성 하여 전역 배열에 액세스하거나 수정할 수 없습니다. bash 4에서는 선언 -g를 사용하여 전역 변수를 선언 할 수 있지만 bash 4에서는 우선이 해결 방법을 피하면서 연관 배열을 사용할 수 있습니다.

요약:

  • bash 4로 업그레이드하고 declare -A연관 배열에 사용하십시오.
  • declare업그레이드 할 수없는 경우이 옵션을 사용하십시오 .
  • awk대신 사용 하고 문제를 피하십시오.

답변

매개 변수 대체가 있지만 PC와 같지 않을 수도 있지만 간접적입니다.

#!/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 방법은 물론 더 좋지만, 해킹이 필요한 경우 … 해킹 만 가능합니다. 비슷한 기술로 배열 / 해시를 검색 할 수 있습니다.


답변

이것이 내가 찾고 있던 것입니다.

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

이것은 bash 4.1.5에서 작동하지 않았습니다.

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


답변

다음과 같이 해시 이름을 지정하도록 hput () / hget () 인터페이스를 추가로 수정할 수 있습니다.

hput() {
    eval "$1""$2"='$3'
}

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

그리고

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

이를 통해 충돌하지 않는 다른지도를 정의 할 수 있습니다 (예 : 수도별로 국가를 조회하는 ‘rcapitals’). 그러나 어느 쪽이든, 나는 이것이 모두 끔찍하고 성능면이라는 것을 알게 될 것입니다.

빠른 해시 조회를 원한다면 실제로 실제로 작동하는 끔찍하고 끔찍한 해킹이 있습니다. 이것은 키 / 값을 임시 파일에 한 줄에 쓴 다음 ‘grep “^ $ key”를 사용하여 잘라내거나 awk 또는 sed 또는 값을 검색하는 파이프를 사용하여 가져옵니다.

내가 말했듯이, 그것은 끔찍하게 들리며 느리게 들리고 모든 종류의 불필요한 IO를 해야하는 것처럼 들리지만 실제로는 매우 큰 해시에서도 매우 빠릅니다 (디스크 캐시는 훌륭하고 그렇지 않습니까?). 테이블. 키 고유성을 직접 적용해야합니다. 수백 개의 항목 만 있어도 출력 파일 / grep 콤보는 약간 더 빠릅니다. 내 경험상 몇 배 더 빠릅니다. 또한 적은 메모리를 사용합니다.

이를 수행하는 한 가지 방법이 있습니다.

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`


답변

파일 시스템 만 사용하십시오

파일 시스템은 해시 맵으로 사용할 수있는 트리 구조입니다. 해시 테이블은 임시 디렉토리가되고 키는 파일 이름이되고 값은 파일 내용이됩니다. 장점은 거대한 해시 맵을 처리 할 수 ​​있으며 특정 쉘이 필요하지 않다는 것입니다.

해시 테이블 생성

hashtable=$(mktemp -d)

요소 추가

echo $value > $hashtable/$key

요소를 읽으십시오

value=$(< $hashtable/$key)

공연

물론 느리지 만 그렇게 느리지는 않습니다 . SSD와 btrfs를 사용하여 내 컴퓨터에서 테스트했으며 초당3000 개의 요소 읽기 / 쓰기를 수행 합니다.


답변

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


답변

다음 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