다음과 같은 중복 항목이 포함된 배열이 있습니다.
THE_LIST=(
"'item1' 'data1 data2'"
"'item1' 'data2 data3'"
"'item2' 'data4'"
)
itemN
위 내용을 바탕으로 연관배열을 생성하여 키와 값으로 지정하고 싶습니다 dataN
.
내 코드는 목록을 반복하고 다음과 같이 key => 값을 할당합니다(추가 기능은 목록에서 일부 추가 작업을 수행하기 때문에 단축됩니다).
function get_items(){
KEY=$1
VALUES=()
shift $2
for VALUE in "$@"; do
VALUES[${#VALUES[@]}]="$VALUE"
done
}
declare -A THE_LIST
for ((LISTID=0; LISTID<${#THE_LIST[@]}; LISTID++)); do
eval "LISTED_ITEM=(${THE_LIST[$LISTID]})"
get_items "${LISTED_ITEM[@]}"
THE_LIST=([$KEY]="${VALUES[@]}")
done
배열을 인쇄하면 다음과 같은 결과가 나타납니다.
item1: data1 data2
item1: data2 data3
item2: data4
하지만 그 대신에 나는 다음을 얻고 싶습니다:
item1: data1 data2 data3
item2: data4
중복 키를 병합하고 키의 중복 값을 제거하는 방법을 찾을 수 없습니다.
여기서 접근 방식은 무엇입니까?
고쳐 쓰다
실제 코드는 다음과 같습니다:
THE_LIST=(
"'item1' 'data1 data2'"
"'item1' 'data2 data3'"
"'item2' 'data4'"
)
function get_backup_locations () {
B_HOST="$2"
B_DIRS=()
B_DIR=()
shift 2
for B_ITEM in "$@"; do
case "$B_ITEM" in
-*) B_FLAGS[${#B_FLAGS[@]}]="$B_ITEM" ;;
*) B_DIRS[${#B_DIRS[@]}]="$B_ITEM" ;;
esac
done
for ((B_IDX=0; B_IDX<${#B_DIRS[@]}; B_IDX++)); do
B_DIR=${B_DIRS[$B_IDX]}
...do stuff here...
done
}
function get_items () {
for ((LOCIDY=0; LOCIDY<${#LOCATIONS[@]}; LOCIDY++)); do
eval "LOCATION=(${LOCATIONS[$LOCIDY]})"
get_backup_locations "${LOCATION[@]}"
THE_LIST=([$B_HOST]="${B_DIR[@]}")
done | sort | uniq
}
배열을 인쇄할 때:
for i in "${!THE_LIST[@]}"; do
echo "$i : ${THE_LIST[$i]}"
done
알겠어요
item1: data1 data2
item1: data2 data3
item2: data4
답변1
키와 값이 순전히 영숫자로 보장된다면 다음과 같이 작동할 수 있습니다.
declare -A output
make_list() {
local IFS=" "
declare -A keys # variables declared in a function are local by default
for i in "${THE_LIST[@]}"
do
i=${i//\'/} # since everything is alphanumeric, the quotes are useless
declare -a keyvals=($i) # split the entry, filename expansion isn't a problem
key="${keyvals[0]}" # get the first value as the key
keys["$key"]=1 # and save it in keys
for val in "${keyvals[@]:1}"
do # for each value
declare -A "$key[$val]=1" # use it as the index to an array.
done # Duplicates just get reset.
done
for key in "${!keys[@]}"
do # for each key
declare -n arr="$key" # get the corresponding array
output["$key"]="${!arr[*]}" # and the keys from that array, deduplicated
done
}
make_list
declare -p output # print the output to check
샘플 입력을 사용하면 다음과 같은 출력을 얻습니다.
declare -A output=([item1]="data3 data2 data1" [item2]="data4" )
데이터 항목의 순서가 잘못되었지만 중복이 제거되었습니다.
아마도 csv
모듈과 함께 Python을 사용하는 것이 가장 좋습니다.
답변2
이 솔루션은 값에 공백이 없으면 작동할 수 있습니다. 연관 배열을 사용하여 명령을 awk
작성합니다 declare -A
.
#!/bin/bash
THE_LIST=(
"'item1' 'data1 data2'"
"'item1' 'data2 data3'"
"'item2' 'data4'"
)
eval "$(\
for i in "${THE_LIST[@]}"; do
row=($(eval echo $i))
echo "${row[@]}"
done | awk '{ for (i=2; i<=NF; i++) if (seen[$1] !~ $i) { seen[$1]=seen[$1]$i" " } }
END { for (s in seen) print "declare -A new_list["s"]=\""seen[s] }' | sed 's/[[:space:]]*$/"/'
)"
for i in "${!new_list[@]}"; do
echo "$i: ${new_list[$i]}"
done
이것은 다음을 인쇄합니다:
item2: data4
item1: data1 data2 data3
값의 순서는 유지되지만 키의 순서가 변경됩니다. 배열 항목의 후행 공백을 자르는 방법을 몰라서 awk
그냥 sed
따옴표로 바꿨는데, 그건 이미 완전한 해킹입니다.