Bash 또는 zsh에서 연관 배열을 정렬하는 올바른 방법은 무엇입니까?

Bash 또는 zsh에서 연관 배열을 정렬하는 올바른 방법은 무엇입니까?

Bash에서 연관 배열을 정렬하는 방법을 알고 싶습니다. 매뉴얼을 시도해 보았지만 정렬과 관련이 없는 것 같습니다.

현재 해결책은 모든 것을 에코하고 외부 프로그램을 사용하는 것입니다.key value | sort -k2

이것은 나에게 매우 비효율적으로 보입니다.

배열의 예는 다음과 같습니다.

A['192.168.2.2']=5
A['192.168.3.2']=1
A['192.168.1.1']=9

가장 일반적인 IP 주소 2개인 192.168.1.1과 192.168.2.2를 찾으겠습니다. 즉, 해당 값을 기준으로 배열을 정렬해야 합니다.

답변1

Zsh에는 목록을 정렬하는 방법이 내장되어 있습니다. 하지만 키와의 관련성을 유지하면서 값을 정렬하는 방법은 없는 것 같습니다.매개변수 확장 플래그그리고아래 첨자 기호, 이는 명시적인 루프가 필요함을 의미합니다. 값에 null 문자가 포함되어 있지 않다고 가정하면 null 문자와 연결된 값과 키의 배열을 만든 다음 정렬할 수 있습니다.

keys=("${(@k)A}")
values=("${(@v)A}")
combined=()
for ((i=1; i <= $#values; i++)) { combined[i]=($values[i]$'\0'$keys[i]); }
keys_sorted_by_decreasing_value=("${${(@On)combined}#*$'\0'}")
keys_of_the_top_two_values=("${(@)keys_sorted_by_decreasing_value[1,2]}")

@sch 편집: 처음 4줄은 다음과 같이 단순화할 수 있습니다.

combined=()
for k v ("${(@kv)A}") combined+=($v$'\0'$k)

변수는 keys임의적이지만 일관된 순서로 values키와 값을 포함합니다. 빈 키가 없으면 A쓸 수 있고 값은 비슷합니다. 키를 사전식으로 정렬하고, 숫자로 정렬하려면 표시를 추가하고(이전), 오름차순으로 정렬하려면 표시를 제거합니다(이 경우 아래 첨자를 사용하여 처음 두 값을 가져올 수 있습니다).keys=(${(k)A})keys_sorted_by_decreasing_valuen910O[-2,-1]

Ksh93에는 위치 인수만 정렬하는 방법이 있습니다 set -s. 이는 zsh에도 있지만 bash 4.2에는 없습니다. 값에 개행 문자나 개행 문자 앞에 정렬되는 제어 문자가 포함되어 있지 않다고 가정합니다.

keys=("${!A[@]}")
combined=()
for ((i=0; i <= ${#keys}; i++)); do combined[i]=(${A[${keys[$i]}]}$'\n'${keys[$i]}); done
set -A sorted -s "${combined[@]}"
top_combined=${sorted[${#sorted[@]}-1]}  # -2 for the next-to-largest, etc.
top_key=${top_combined#*$'\n'}

이것은 모두 매우 복잡하므로 작성하기 쉬운 외부 ​​정렬을 사용하는 것이 좋습니다. ksh 또는 bash의 키나 값에 제어 문자가 포함되어 있지 않다고 가정합니다.

IFS=$'\n'; set -f
keys_sorted_by_decreasing_value=($(
    for k in "${!A[@]}"; do printf '%s\t%s\n' "${A[$k]}" "$k"; done |
    sort | sed $'s/\t.*//'
  ))

답변2

${(kOn)A}zsh에서는 연관 배열( ) 또는 값( ) 의 정렬된 키 목록을 얻을 수 있지만 ${(On)A}정렬된 값 목록(AFAIK)에서 직접 얻을 수는 없지만 다음을 수행할 수 있습니다.

typeset -A assoc
assoc=(
  192.168.2.2 5
  192.168.3.2 1
  192.168.1.1 9
  192.168.8.1 9
)
ordered_keys=()

for v ("${(@nO)assoc}") ordered_keys+=("${(@k)assoc[(eR)$v]}")

즉, 값 목록()을 숫자()로 정렬하고 각 값()에 대해 O일치하는 ey를 추가하고(정확한 일치의 경우 키가 아닌 값을 기반으로 역방향 목록을 가져옴) 추가합니다. 배열.$assocnforvkeRordered_keys

답변3

KEY로 bash 연관 배열을 정렬하는 가장 좋은 방법은 다음과 같습니다.아니요분류하세요.

대신 KEYS 목록을 가져와 목록을 변수로 정렬한 다음 목록을 반복합니다. 예: IP 주소(키)와 호스트 이름(값) 배열이 있다고 가정합니다.

대안: KEY에서 새 목록을 생성하고, 행으로 변환하고, 정렬하고, 다시 목록으로 변환한 다음 이를 사용하여 배열을 반복합니다.

declare -A ADDR
ADDR[192.168.1.1]="host1"
ADDR[192.168.1.2]="host2"
etc...

KEYS=`echo ${!ADDR[@]} | tr ' ' '\012' | sort | tr '\012' ' '`
for KEY in $KEYS; do
  VAL=${ADDR[$KEY]}
  echo "KEY=[$KEY] VAL=[$VAL]"
done

답변4

"연관 배열"은 일반적으로 배열의 데이터가 실제 의미를 갖는다는 것을 의미합니다. 외부 유닉스 정렬은 이 작업에 매우 적합하며, 유닉스 정렬을 능가할 수 있는 C 프로그래머는 거의 없습니다. 특히 빅 데이터의 경우 UNIX와 셸의 모든 기능을 사용자 정의하고, 슬라이싱하고, 분기하고, 최대한 활용할 수 있습니다. 이것이 바로 많은 쉘 및 awk 플랫폼이 정렬에 신경 쓰지 않는 이유입니다.

관련 정보