Pengembalian tidak langsung semua elemen dalam array

9

Halaman Bash man menjelaskan penggunaan ${!a}untuk mengembalikan konten variabel yang namanya isi a(tingkat tipuan).

Saya ingin tahu cara mengembalikan semua elemen dalam array menggunakan ini, yaitu,

a=(one two three)
echo ${a[*]}

kembali

one two three

Saya ingin untuk:

b=a
echo ${!b[*]}

untuk mengembalikan yang sama. Sayangnya, tidak, tetapi kembali 0sebagai gantinya.

Memperbarui

Mengingat jawaban, saya sekarang menyadari bahwa contoh saya terlalu sederhana, karena tentu saja, seperti:

b=("${a[@]}")

Akan mencapai apa yang saya katakan saya butuhkan.

Jadi, inilah yang saya coba lakukan:

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST=LIST_$whichone
LIST=${!_LIST[*]}

Tentu saja, dengan hati-hati membaca halaman manual Bash menunjukkan bahwa ini tidak akan berfungsi seperti yang diharapkan karena baris terakhir hanya mengembalikan indeks "array" $_LIST(bukan array sama sekali).

Dalam kasus apa pun, yang berikut harus melakukan pekerjaan (seperti yang ditunjukkan):

LIST=($(eval echo \${$_LIST[*]}))

atau ... (rute yang saya tempuh, pada akhirnya):

LIST_lys="lys1 lys2"
...
LIST=(${!_LIST})

Dengan asumsi, tentu saja, elemen tidak mengandung spasi.

Eric Smith
sumber
Tambahkan [@]ke pointer _LIST="LIST_${whichone}[@]"dan kemudian, gunakan LIST=("${!_LIST}")untuk menyalin array. Sebaiknya gunakan nama variabel dengan huruf kecil untuk menghindari konflik dengan variabel lingkungan (Semua huruf besar).
Isaac

Jawaban:

6

Saya pikir penggunaan referensi tidak langsung dari variabel bash harus diperlakukan secara harfiah.

Misalnya. Untuk contoh asli Anda:

a=(one two three)
echo ${a[*]} # one two three
b=a
echo ${!b[*]} # this would not work, because this notation 
              # gives the indices of the variable b which
              # is a string in this case and could be thought
              # as a array that conatins only one element, so
              # we get 0 which means the first element
c='a[*]'
echo ${!c} # this will do exactly what you want in the first
           # place

Untuk skenario nyata terakhir, saya percaya kode di bawah ini akan berfungsi.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[*]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one

Lebih baik menggunakan notasi "${var[@]}"yang menghindari mengacaukan $IFSekspansi dan parameter. Ini kode terakhirnya.

LIST_lys=(lys1 lys2)
LIST_diaspar=(diaspar1 diaspar2)

whichone=$1   # 'lys' or 'diaspar'

_LIST="LIST_$whichone"[@]
LIST=( "${!_LIST}" ) # Of course for indexed array only 
                     # and not a sparse one
                     # It is essential to have ${!_LIST} quoted
weynhamz
sumber
8

Anda perlu menyalin elemen secara eksplisit. Untuk array yang diindeks:

b=("${a[@]}")

Untuk array asosiatif (perhatikan bahwa itu aadalah nama variabel array, bukan variabel yang nilainya adalah nama variabel array):

typeset -A b
for k in "${!a[@]}"; do b[$k]=${a[$k]}; done

Jika Anda memiliki nama variabel dalam array, Anda bisa menggunakan metode elemen-demi-elemen dengan langkah ekstra untuk mengambil kunci.

eval "keys=(\"\${!$name[@]}\")"
for k in "${keys[@]}"; do eval "b[\$k]=\${$name[\$k]}"; done

(Peringatan, kode dalam posting ini diketik langsung di browser dan tidak diuji.)

Gilles 'SANGAT berhenti menjadi jahat'
sumber
Anda benar, tetapi sayangnya, itu tidak menyelesaikan masalah saya (contoh saya terlalu sederhana). Saya telah memberikan pembaruan agar lebih jelas tentang niat saya.
Eric Smith
@ Eric Saya pikir di ksh / bash Anda perlu eval pada tahap itu. Lihat hasil edit saya.
Gilles 'SO- stop being evil'
+1, tetapi sesuai penafian Anda, ini sebenarnya tidak cukup berfungsi (bit kode terakhir), tetapi hanya perlu beberapa penyesuaian kecil. Secara khusus, itu harus \${!$name[@]}di baris pertama, sehingga ekspansi pertama hanya dari '$ name', dan ${!a[@]}disimpan untuk eval, dan hal yang sama dalam for loop, dengan \${$name}. Dua backslash lainnya sebelum '$ k' juga tidak diperlukan di sana.
krb686
2

${!b[*]}memperluas ke indeks yang digunakan dalam array b.

Apa yang Anda ingin harus dilakukan dalam dua langkah, sehingga evalakan membantu: eval echo \${$b[*]}. (Catat \yang memastikan bahwa yang pertama $akan melewati langkah pertama, ekspansi variabel, dan hanya akan diperluas di langkah kedua oleh eval.)

Menurut Parameter Ekspansi !keduanya digunakan untuk ekspansi tidak langsung ( {!a}), Awalan pencocokan nama ( ${!a*}) dan Daftar kunci array ( ${!a[*]}). Karena Daftar kunci larik memiliki sintaksis yang sama dengan ekspansi elemen + array tidak langsung yang Anda inginkan, yang kemudian tidak didukung seperti apa adanya.

manatwork
sumber
2
${!a}memperluas ke nilai variabel yang namanya $a. Ini agak dijelaskan dalam manual, dalam paragraf yang dimulai dengan "Jika karakter pertama parameter adalah titik seru ( !), level indirection variabel diperkenalkan."
Gilles 'SO- stop being evil'
Yap - @Gilles benar, tapi @manatwork, pada bacaan kedua, saya perhatikan bahwa ${!itu agak tidak sopan karena jika Anda menggunakan array, tingkah lakunya berbeda.
Eric Smith
@Gilles Anda benar pada kalimat itu, tetapi sayangnya itu tidak berlaku sebagai "Pengecualian untuk ini adalah perluasan dari $ {! Awalan *} dan $ {! Name [@]} yang dijelaskan di bawah." Tetapi balasan saya jelas merupakan kekacauan yang ambigu, jadi saya akan mengeditnya.
manatwork
0

Untuk mengakses array secara tidak langsung, cukup tambahkan [@]ke variabel tidak langsung b=a[@].

Jika variabel ini disetel:

a=(one two three)
printf '<%s> ' "${a[@]}"; echo

Kemudian, ini akan berhasil:

b="a[@]"
printf '<%s> ' "${!b}"

Atau sederhananya:

echo "${!b}"

Array seperti itu dapat disalin sebagai berikut:

newArr=( "${!b}" )

Dan kemudian dicetak dengan:

declare -p newArr

Dalam satu skrip:

#!/bin/bash
a=(one two three)
echo "original array"
printf '<%s> ' "${a[@]}"; echo

echo "Indirect array with variable b=a[@]"
b="a[@]"
printf '<%s> ' "${!b}"; echo

echo "New array copied in newArr, printed with declare"
newArr=( "${!b}" )
declare -p newArr

Tentu saja, semua yang di atas akan menyalin array yang tidak jarang. Satu di mana semua indeks memiliki nilai.

array jarang

Array jarang adalah salah satu yang mungkin memiliki elemen yang tidak didefinisikan.
Misalnya a[8]=1234mendefinisikan satu elemen, dan, dalam bash, 0 hingga 7 tidak ada.

Untuk menyalin array yang jarang, gunakan metode ini

  1. Cetak array lama:

    $ oldarr[8]=1234
    $ declare -p oldarr
    declare -a oldarr=([8]="1234")
    
  2. Ganti nama array dan tangkap string:

    $ str=$(declare -p oldarr | sed 's/oldarr=/newarr=/')
  3. Eval string yang dibuat, array baru telah ditentukan:

    $ eval "$str"
    $ declare -p newarr
    declare -a newarr=([8]="1234")
Ishak
sumber