Tablice asocjacyjne w skryptach powłoki

11

Widziałem sztuczkę polegającą na implementacji tablic asocjacyjnych w skrypcie powłoki. Na przykład print array["apples"]może być skryptowany jako echo \$array$keygdzie klucz = jabłka.

Nie wspomniano jednak o tym, jak wygenerować klucze do iteracji po tablicy. Jedynym sposobem, w jaki mogłem wymyślić, było przechowywanie kluczy w zmiennej ograniczonej spacjami, aby móc użyć pętli for do iteracji po tablicy.

Czy istnieje jakiś inny sposób przechowywania kluczy do późniejszego wykorzystania?

Jajogłowy
źródło
5
Jeśli próbujesz użyć tablic asocjacyjnych w skrypcie powłoki, być może twój projekt jest zbyt skomplikowany, aby można go było użyć w skrypcie powłoki :)
Martin von Wittich
@MartinvonWittich dlaczego? Mam skrypt powłoki, który wykonuje skrypt SQL na jednym z 3 możliwych schematów DB. Wymagany schemat jest zawarty w nazwie pliku ze skrótem. Potrzebuję mapowania między tym skrótem a prawdziwą nazwą schematu. Czy istnieje lepszy sposób niż tablica asocjacyjna, biorąc pod uwagę faktyczne nazwy schematów (nie skrót), mogą się różnić w różnych środowiskach, więc zmienna tablicowa (której wartości można ustawić tylko raz) jest idealna
Slav
2
@Slav Nie spieram się przeciwko tablicom asocjacyjnym, tylko przeciwko skryptom powłoki, które wymagają takiej złożoności. Ale to tylko moje osobiste preferencje; Często łapię się na tym, że zaczynam pisać skrypt powłoki, a potem natychmiast przepisuję go w Perlu, kiedy zdaję sobie sprawę, że przekraczam pewien próg złożoności.
Martin von Wittich,

Odpowiedzi:

20

Pociski z tablicami asocjacyjnymi

Niektóre nowoczesne powłoki zapewniają tablice asocjacyjne: ksh93, bash ≥4, zsh. W ksh93 i bash, jeśli ajest tablicą asocjacyjną, "${!a[@]}"to tablica jej kluczy:

for k in "${!a[@]}"; do
  echo "$k -> ${a[$k]}"
done

W zsh ta składnia działa tylko w trybie emulacji ksh. W przeciwnym razie musisz użyć natywnej składni zsh:

for k in "${(@k)a}"; do
  echo "$k -> $a[$k]"
done

${(k)a}działa również, jeśli anie ma pustego klucza.

W Zsh możesz także zapętlać jednocześnie keys i value:

for k v ("${(@kv)a}") echo "$k -> $v"

Pociski bez tablic asocjacyjnych

Emulowanie tablic asocjacyjnych w powłokach, które ich nie mają, to dużo więcej pracy. Jeśli potrzebujesz tablic asocjacyjnych, prawdopodobnie czas wprowadzić większe narzędzie, takie jak ksh93 lub Perl.

Jeśli potrzebujesz tablic asocjacyjnych w zwykłej powłoce POSIX, oto sposób ich symulacji, gdy klucze są ograniczone tylko do znaków 0-9A-Z_a-z(cyfry ASCII, litery i podkreślniki). Przy takim założeniu klucze mogą być używane jako część nazw zmiennych. Poniższe funkcje działają na tablicę identyfikowaną przez przedrostek nazewnictwa „rdzeń”, który nie może zawierać dwóch kolejnych znaków podkreślenia.

## ainit STEM
## Declare an empty associative array named STEM.
ainit () {
  eval "__aa__${1}=' '"
}
## akeys STEM
## List the keys in the associatve array named STEM.
akeys () {
  eval "echo \"\$__aa__${1}\""
}
## aget STEM KEY VAR
## Set VAR to the value of KEY in the associative array named STEM.
## If KEY is not present, unset VAR.
aget () {
  eval "unset $3
        case \$__aa__${1} in
          *\" $2 \"*) $3=\$__aa__${1}__$2;;
        esac"
}
## aset STEM KEY VALUE
## Set KEY to VALUE in the associative array named STEM.
aset () {
  eval "__aa__${1}__${2}=\$3
        case \$__aa__${1} in
          *\" $2 \"*) :;;
          *) __aa__${1}=\"\${__aa__${1}}$2 \";;
        esac"
}
## aunset STEM KEY
## Remove KEY from the associative array named STEM.
aunset () {
  eval "unset __aa__${1}__${2}
        case \$__aa__${1} in
          *\" $2 \"*) __aa__${1}=\"\${__aa__${1}%%* $2 } \${__aa__${1}#* $2 }\";;
        esac"
}

(Ostrzeżenie, nieprzetestowany kod. Wykrywanie błędów dla składniowo niepoprawnych rdzeni i kluczy nie jest zapewnione).

Gilles „SO- przestań być zły”
źródło
5

Nie jestem pewien, co masz na myśli mówiąc o sklepie, ale możesz iterować po kluczach, używając ${!array[@]}składni:

$ typeset -A foo=([key1]=bar [key2]=baz);
$ echo "${!foo[@]}" 
key2 key1

Tak więc, aby iterować:

$ for key in "${!foo[@]}"; do echo "$key : ${foo[$key]}"; done
key2 : baz
key1 : bar

Tutaj znalazłem ładny, krótki samouczek .


Jak wskazano w komentarzach poniżej, tablice asocjacyjne zostały dodane w bashwersji 4. Zobacz tutaj artykuł w dzienniku Linux na ten temat.

terdon
źródło
1
(bash version 4 only)To ważna rzecz do zapamiętania. Tradycyjnie bashtablice są tylko numeryczne.
Ricky Beam
1
Możesz użyć typesetzamiast declarew swoich przykładach. Dzięki temu byłyby przenośne między bash 4 a ksh93, które jako pierwsze zaimplementowały tablice asocjacyjne powłoki.
jlliagre
0

Pociski bez tablic asocjacyjnych

Nie jest to takie trudne, gdy klawisze są ograniczone do [0-9A-Za-z_](cyfr, liter, podkreślenia).

Sztuczka polega na tym, że zamiast zapisywać w tablicy [ $ key ], przechowywać w zmiennych tablica_ $ key .

Zestaw:

eval "array_$key='$value'"

Dostać:

value=`eval echo '$'array_$key`

Uwaga: Wartości nie mogą zawierać '(pojedynczy cytat).

Marián Černý
źródło
-1

to działa w trybie bash

cert="first"
web="second"
declare -A assoc_array=(["cert"]="${cert}" ["web"]="${web}")
echo "first is" ${assoc_array[cert]}
echo "second is" ${assoc_array[web]}

LUB

#loop
for i in "${assoc_array[@]}"
do
   echo "$i"
done

Nie trzeba używać eval afaik

JamesD
źródło
1
Uważam, że nie trafiłeś w sedno pytania.
G-Man mówi „Przywróć Monikę”