Referensi variabel array bash dari array lain

8

Saya ingin menulis skrip untuk referensi beberapa array dari array lain yang menyimpan nama-nama variabel array ini.

Inilah kode saya sejauh ini:

#!/bin/bash
array1=('array1string1' 'array1string2')
array2=('array2string1' 'array2string2')

array_names=('array1' 'array2')

for a in ${array_names[@]}
do
        for b in ${a[@]}
        do
                echo $b
        done
done

Saya ingin output memindai kedua array (dari luar untuk loop) dan mencetak masing-masing string di dalam untuk loop yang memanggil gema. Output saya saat ini hanya menunjukkan kepada saya:

array1
array2

Saya akan berterima kasih atas petunjuk tentang hal ini. Terima kasih!

chnppp
sumber
Apakah ada alasan mengapa Anda tidak bisa begitu saja melakukannya for b in "${array1[@]}" "${array2[@]}"; do ...; done?
Kusalananda
Saya ingin jumlah array fleksibel. Oleh karena itu, jika saya menambahkan sebuah array nanti, saya hanya akan menambahkannya ke array_names dan membiarkan loop mengurusnya.
chnppp
Saya pikir ini adalah kasus untuk ekspansi tidak langsung. Lihat stackoverflow.com/questions/8515411/… - tetapi, pada dasarnya, berubah ${a[@]}untuk ${!a}melakukan apa yang Anda inginkan (saya pikir).
parkamark
1
@parkamark Tidak, itu hanya memberinya elemen pertama dari setiap array. Dan ${!a[@]}memberikan panjang array a.
Kusalananda
Ya, berubah menjadi ${!a}hanya memberi saya elemen pertama.
chnppp

Jawaban:

7

Bash 4.3 dan yang lebih baru mendukung "referensi nama", atau nameref (ada konsep yang serupa ksh93, tetapi pelingkupannya sangat berbeda ):

#!/bin/bash

array1=('array1string1' 'array1string2')
array2=('array2string1' 'array2string2')

array_names=('array1' 'array2')

for a in "${array_names[@]}"; do
    declare -n arr="$a"

    for b in "${arr[@]}"; do
        echo "$b"
    done
done

Variabel arradalah nameref yang bertindak seperti alias untuk variabel bernama (variabel dengan nama $adalam contoh ini).

Tanpa namerefs, di versi Bash sebelumnya, salah satu solusinya adalah membuat array baru yang berisi semua elemen dari array lain:

all=( "${array1[@]}" "${array2[@]}" )

... sedikit seperti array_namesarray dalam pertanyaan tetapi dengan isi semua array, dan kemudian beralih "${all[@]}".

Itu juga mungkin untuk digunakan eval, tetapi kode yang dihasilkan tampak sangat mengerikan.

Lihat jawaban glenn jackman untuk variasi dengan tipuan variabel (diperkenalkan dalam bentuk saat ini dengan Bash versi 2).

Kusalananda
sumber
3

@ Kusalananda memiliki jawaban terbaik untuk versi bash terbaru. Untuk versi sebelumnya, Anda dapat menggunakan variabel tidak langsung:

for a in ${array_names[@]}; do 
    tmp="${a}[@]"
    for b in "${!tmp}"; do echo "$b"; done     # or: printf "%s\n" "${!tmp}"
done

Lihat paragraf ke-4 https://www.gnu.org/software/bash/manual/bashref.html#Shell-Parameter-Expansion

glenn jackman
sumber
0

Sebagai variasi dari apa yang dikatakan:

#!/bin/bash

array1=('array1 string1' 'array1 string2')
array2=('array2 string1' 'array2 string2')
array_names=('array1' 'array2')

for (( i=0; i<${#array_names[@]}; i++ )); do

    declare -n arr="${array_names[i]}"

    for (( j=0; j<${#arr[@]}; j++ )); do
        echo "${arr[j]}"
    done

done

Mengakses elemen dengan indeks sebagai gantinya

chris
sumber