web-dev-qa-db-ja.com

シェルでkey:arrayのマップを作成する方法は?

シェルで地図を作成したいのですが。各値は配列です。したがって、マップはkey:arrayペアです。たとえば、次のようになります。

"Key1" : a1 a2 a3 a4
"key2" : b1 b2 b3
"key3" : c1

基本的に私のコードは次のようになります

listService(){
serviceType=$1
servicesList=($(getServices $serviceType))
}

listService serviceTypeA
listService serviceTypeB
listService serviceTypeC

ここでgetServicesは、$serviceTypeとして渡された引数に基づいてサービスの配列を返す関数です。したがって、listService関数を呼び出すたびに、私のserviceListが新しいサービスリストによって上書きされます。しかし、私は次のようなマップの形で異なるサービスタイプのすべてのサービスを保持したいと思います。

"serviceA" : a1 a2 a3 a4
"serviceB" : b1 b2 b3
"serviceC" : c1

その後、キーに基づいて各アレイにアクセスします。これを達成する方法。

よろしくお願いします。

編集:@cdarkeが提供する答えを試しました。これが私のコードです:

#!/bin/bash
declare -A arrayMap

getValues(){
  key=$1
  case $key in
    AAA )
    arr=( AA AAA AAAA )
      ;;
    BBB )
    arr=( BB BB BBBB )
      ;;
    CCC )
    arr=()
    ;;
    esac
    echo "${arr[@]}"
}

fillArrayMap(){
  param=$1
  values=( $(getValues $param) )
  printf "\nIn $param\n"
  echo -e "\nArray values is: ${values[@]}\n"
  printf "\nLength of the array values is : ${#values[@]}\n"
  arrayMap["$param"]=$values #THIS IS THE KEY LINE
  valuesList=${arrayMap[$param]} 
  echo -e "\nArray valuesList is: ${valuesList[@]}\n"
  printf "\nLength of the array valuesList is : ${#valuesList[@]}\n"
}

fillArrayMap AAA
fillArrayMap BBB
fillArrayMap CCC

出力から、valuesListvalues配列の最初の要素のみを取得していることがわかります。しかし、私はvaluesListに、メソッドgetValuesによって返されるすべての要素を含めたいと思います。つまり

valuesList= ${arrayMap[$param]}

valuesListにはすべての要素が含まれているはずですが、代わりに1つの要素しか含まれていません。それを修正するには?

注:私の目標は、AAAやAAのような個々の要素にアクセスすることです。AAAAA AAAAのような文字列として全体としてそれを必要としません

8
saurav

Bashは多次元配列をサポートしていませんが、多次元配列は必要ないと思います。文字列をリストの形で配列要素に格納できます。これにより、必要なものが得られます。

# My made-up version of getServices
getServices() {
    nm="$1"
    last=${nm##*Type}
    retn=(${last}1 ${last}2 ${last}3 ${last}4)
    echo "${retn[@]}"
}


declare -A serviceList
listService(){
    serviceType="$1"

    # Here I use the key to make an assignment, which adds to the hash
    serviceList["$serviceType"]=$(getServices $serviceType) 
}

listService serviceTypeA
listService serviceTypeB
listService serviceTypeC

for key in ${!serviceList[@]}
do
    echo "\"$key\": ${serviceList[$key]}"
done

与える:

"serviceTypeC": C1 C2 C3 C4
"serviceTypeB": B1 B2 B3 B4
"serviceTypeA": A1 A2 A3 A4

新しい質問の編集:

変更:

arrayMap["$param"]=$values     # THIS IS THE KEY LINE
valuesList=${arrayMap[$param]} 

に:

arrayMap["$param"]=${values[@]} 
valuesList=( ${arrayMap[$param]}  )  

配列変数をその名前($values最初の要素のみを取得します。

4
cdarke

Cdarkeが既に述べたように、bash配列は1次元です。長年にわたって、人々は多次元配列を「偽造」する方法を考え出しました。

私が使用した2つの方法は、配列記述の配列を維持する、または他の配列へのポインタの配列を維持することです。前者で答えます。自分で探索したい場合は、後者は明らかです。

以下は、配列のコンテンツが変数の入力に使用される最小限の例です。

#!/usr/bin/env bash

declare -A a=(
  [b]='([0]="one" [1]="two")'
  [c]='([0]="three" [1]="four")'
)

declare -p a

for key in ${!a[@]}; do
  declare -a $key="${a[$key]}"
  declare -p $key
done

生成:

declare -A a=([b]="([0]=\"one\" [1]=\"two\")" [c]="([0]=\"three\" [1]=\"four\")" )
declare -a b=([0]="one" [1]="two")
declare -a c=([0]="three" [1]="four")

ここで重要なのは、declareを使用して$keyの値を参照していることです。これは、bashで$var="value"とだけ言うことはできないためです。

もちろん、必要がない場合は、変数に$keyの値の名前を付ける必要はありません。たとえば$valueに値を格納すると、$keyで特殊文字を使用できるようになります。

感性を害したり、キー名を制限したりしなければ、さらに単純な代替策は、declare -pコマンドの出力全体を配列の値に格納し、次にevalを使用することです。必要なときにそれを。例えば:

declare -A a=(
 [b]='declare -a b=([0]="one" [1]="two")'
 [c]='declare -a c=([0]="three" [1]="four")'
)

for key in ${!a[@]}; do
  eval "${a[$key]}"
done

evalを嫌う人もいます。 :-)それは残りますが、ツールボックスには残ります。

あなたの場合、完全な [〜#〜] mcve [〜#〜] を提供していないため、アドバイスするのは少し難しいですが、これが私の不自然な例です。

#!/usr/bin/env bash

# contrived getServices function, since you didn't provide one
getServices() {
    local -a value=()
    local last="${1:$((${#1}-1)):1}"   # last character of $1
    for n in $( seq 1 $(( $RANDOM / 8192 + 1 )) ); do
      value+=(${last}${n})
    done
    declare -p value     # output of this function is actual bash code.
}

# populate the array
listService() {
    servicesList[$1]=$( getServices $1 )
}

# Initialize this as empty to make `eval` safer
declare -A servicesList=()

# These services seem interesting.
listService serviceA
listService serviceB
listService serviceC

# Note that we're stepping through KEYS here, not values.
for row in "${!servicesList[@]}"; do
    printf '"%s": ' "$row"
    eval "${servicesList[$row]}"   # Someone is bound to complain about this.
    for column in "${!value[@]}"; do
        # Add whatever $row and $column specific code you like here.
        printf '%s ' "${value[$column]}"
    done
    printf "\n"
done

私の出力:

$ bash 2dimarrayexample
"serviceC": C1
"serviceB": B1 B2 B3 B4
"serviceA": A1 A2

もちろん、getServicesはランダムな出力を生成するため、出力は異なる場合があります。 :)

3
ghoti