Array Asosiatif dalam Skrip Shell

11

Saya melihat trik untuk mengimplementasikan array asosiatif dalam skrip shell. Misalnya print array["apples"]bisa dituliskan sebagai echo \$array$keymana kunci = apel.

Namun, tidak disebutkan bagaimana menghasilkan kunci untuk beralih di atas array. Satu-satunya cara saya bisa memikirkan adalah untuk menyimpan kunci dalam variabel yang dibatasi oleh spasi sehingga saya bisa menggunakan for-loop untuk beralih pada array.

Jadi, apakah ada cara lain untuk menyimpan kunci untuk digunakan nanti?

Orang terpelajar
sumber
5
Jika Anda mencoba menggunakan array asosiatif dalam skrip shell, mungkin proyek Anda terlalu kompleks untuk skrip shell :)
Martin von Wittich
@MartinvonWittich mengapa? Saya memiliki skrip shell yang mengeksekusi skrip SQL pada salah satu dari 3 skema DB yang mungkin. Skema yang diperlukan termasuk dalam nama file dengan singkatan. Saya perlu pemetaan antara singkatan ini dan nama skema yang sebenarnya. Apa cara yang lebih baik daripada array asosiatif, mengingat nama skema yang sebenarnya (bukan singkatan) dapat berbeda di antara lingkungan, sehingga variabel array (yang nilainya dapat diatur hanya sekali) sempurna
Slav
2
@ Slav Saya tidak menentang array asosiatif, hanya terhadap skrip shell di mana kompleksitas seperti itu diperlukan. Tapi itu hanya preferensi pribadi saya; Saya sering menemukan diri saya mulai menulis skrip shell dan kemudian segera menulis ulang di Perl ketika saya menyadari bahwa saya melebihi ambang batas kompleksitas tertentu.
Martin von Wittich

Jawaban:

20

Kerang dengan array asosiatif

Beberapa shell modern menyediakan array asosiatif: ksh93, bash ≥4, zsh. Di ksh93 dan bash, if aadalah array asosiatif, maka "${!a[@]}"adalah array kuncinya:

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

Di zsh, sintaks itu hanya berfungsi dalam mode emulasi ksh. Kalau tidak, Anda harus menggunakan sintaks asli zsh:

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

${(k)a}juga berfungsi jika atidak memiliki kunci kosong.

Di zsh, Anda juga bisa mengulangi kedua mata kdan vmata sekaligus:

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

Kerang tanpa array asosiatif

Meniru array asosiatif dalam cangkang yang tidak memilikinya jauh lebih banyak pekerjaan. Jika Anda membutuhkan array asosiatif, mungkin inilah saatnya untuk membawa alat yang lebih besar, seperti ksh93 atau Perl.

Jika Anda membutuhkan array asosiatif dalam shell POSIX belaka, inilah cara untuk mensimulasikannya, ketika kunci dibatasi untuk hanya memuat karakter 0-9A-Z_a-z(ASCII digit, huruf, dan garis bawah). Di bawah asumsi ini, kunci dapat digunakan sebagai bagian dari nama variabel. Fungsi di bawah ini bekerja pada array yang diidentifikasi oleh awalan penamaan, "batang", yang tidak boleh berisi dua garis bawah berturut-turut.

## 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"
}

(Peringatan, kode yang belum diuji. Deteksi kesalahan untuk batang dan kunci secara sintaksis tidak disediakan.)

Gilles 'SANGAT berhenti menjadi jahat'
sumber
5

Saya tidak yakin apa yang Anda maksud dengan toko, tetapi Anda dapat beralih menggunakan kunci menggunakan ${!array[@]}sintaks:

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

Jadi, untuk beralih:

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

Saya menemukan tutorial singkat yang bagus tentang ini di sini .


Seperti yang ditunjukkan dalam komentar di bawah ini, array asosiatif ditambahkan dalam bashversi 4. Lihat di sini untuk artikel jurnal Linux tentang masalah ini.

terdon
sumber
1
(bash version 4 only)Itu hal yang penting untuk diperhatikan. Secara tradisional, basharray hanya numerik.
Ricky Beam
1
Anda mungkin ingin menggunakan, typesetbukan declaredalam contoh Anda. Itu akan membuatnya portabel antara bash 4 dan ksh93 yang pertama kali mengimplementasikan array asosiatif shell.
jlliagre
0

Kerang tanpa array asosiatif

Tidak sulit ketika tombol dibatasi [0-9A-Za-z_](angka, huruf, garis bawah).

Caranya adalah alih-alih menyimpan ke array [ $ key ], simpan ke variabel array_ $ key .

Set:

eval "array_$key='$value'"

Dapatkan:

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

Catatan: Nilai tidak dapat mengandung '(kutipan tunggal).

Marián Černý
sumber
-1

ini bekerja di 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]}

ATAU

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

Tidak perlu menggunakan eval afaik

JamesD
sumber
1
Saya yakin Anda telah melewatkan inti pertanyaan.
G-Man Mengatakan 'Reinstate Monica'