Gunakan referensi variabel "di dalam" variabel lain

27

Saya yakin itu relatif sederhana, saya hanya tidak tahu bagaimana melakukannya.

#!/usr/bin/ksh
set `iostat`
myvar=6

Saya ingin sesuatu echo ${$myvar}yang ingin saya artikan sebagai ${$myvar}-> ${6}->value

Brandon Kreisel
sumber
4
Istilah teknisnya adalah variabel tipuan .
Thor

Jawaban:

29

Anda dapat melakukan ini dengan eval, bawaan ke banyak cangkang halus, termasuk ksh:

#!/usr/bin/ksh
set $(iostat)
myvar=6
eval "echo \${$myvar}"

Caranya adalah dengan mengutip dua kali string yang Anda beri makan evalsehingga $ myvar diganti dengan "6", dan untuk backslash tanda luar dolar, sehingga evalmendapat string "$ 6".

Saya mendapat "% pengguna" untuk output, tetapi saya mencobanya pada mesin RHEL multi-prosesor.

Bruce Ediger
sumber
4
Anda secara resmi adalah Maha Guru Besar Agung minggu itu b / c yang bahkan bekerja pada ksh yang sangat buruk (benar-benar pdksh) di OpenBSD 5.4. Jika Anda ingin mengatur var vv ke nilai var yang namanya ada di var vn , lakukan saja vv=$( eval "echo \$$vn" ). Terima kasih banyak!
execNext
25

Referensi variabel tidak langsung

Kerang canggih modern memiliki metode untuk referensi nilai variabel yang namanya disimpan dalam variabel lain. Sayangnya metode ini berbeda antara ksh, bash dan zsh.

Di mksh ≥R39b, Anda dapat membuat myvarnameref:

typeset -n myvar=6
echo "$myvar"

Ini tidak berfungsi di ATT ksh93 karena tidak mendukung namerefs ke parameter posisi. Dalam kasus di mana Anda memiliki variabel yang berisi nama variabel, Anda dapat menggunakan metode ini.

foo=bar
typeset -n myvar=foo
echo "$myvar"  # prints bar

Dalam bash ≥2.0, Anda dapat menulis

echo "${!myvar}"

Di zsh, Anda dapat menulis

echo ${(P)myvar}

Dalam shell yang lebih lama, termasuk ksh88 dan pdksh, satu-satunya jalan Anda ketika Anda memiliki variabel yang berisi nama variabel lain dan ingin menggunakan nilai variabel ini eval, seperti yang dijelaskan oleh Bruce Ediger . Solusi ini berfungsi di semua shell Bourne / POSIX.

eval "value=\${$myvar}"
echo "$value"

Menggunakan array

Ini adalah metode terbaik di sini: lebih sederhana dan lebih portabel.

Untuk kasus penggunaan Anda, dalam setiap shell dengan array (semua varian ksh, bash ≥2.0, zsh), Anda dapat menetapkan ke variabel array dan mengambil elemen yang Anda inginkan. Berhati - hatilah karena array ksh dan bash mulai penomoran pada 0, tetapi zsh dimulai pada 1 kecuali Anda mengeluarkan setopt ksh_arraysatau emulate ksh.

set -A iostat -- $(iostat)
echo "${iostat[5]}"

Jika Anda ingin menyalin parameter posisi ke variabel array a:

set -A a -- "$@"

Di ksh93, mksh ≥R39b, bash ≥2.0 dan zsh, Anda dapat menggunakan sintaks penugasan array:

iostat=($(iostat))
echo "${iostat[5]}"
Gilles 'SANGAT berhenti menjadi jahat'
sumber
Wow, solusi 'Bourne / POSIX' Anda juga berfungsi di ksh / pdksh OpenBSD 5.4. Untuk menerapkannya pada contoh dalam komentar saya untuk jawaban Bruce Ediger di atas, lakukan saja eval "vv=\${$vn}". Merci beaucoup, tuan baik.
execNext
1

Seperti yang ditunjukkan oleh Gilles (yang memberikan bashbagian dari jawaban), juga tidak membatalkan Bruce Ediger (tentang cara melakukannya dengan mudah eval), inilah cara melakukannya dengan namerefbaru-baru ini mksh(dan AT&T ksh93, kecuali - seperti komentar @Gilles - namerefs tidak dapat merujuk ke parameter posisi di AT&T ksh, hanya ke parameter yang disebutkan):

#!/bin/mksh
set -- $(iostat)
nameref myvar=6
echo $myvar

Menambahkan --after setuntuk peningkatan resistensi juga.

mirabilos
sumber
Pada ksh 93u, namerefs tidak dapat merujuk parameter posisi ( typeset: 6: invalid variable name).
Gilles 'SO- stop being evil'
0

Penggunaan array lainnya

Belum pernah menggunakan ksh atau varian apa pun untuk beberapa waktu, jadi saya tidak yakin apakah ksh (atau bash) memiliki kemampuan yang sama. Shell utama saya adalah zsh. Saya menggunakan array ketika menangani output dari perintah seperti iostat karena mereka menghasilkan beberapa baris, dan tidak semua baris memiliki format / panjang yang sama.

#! /bin/zsh
IOStatOutput=("${(@f)$(iostat)}") # Produces one element per line

Di atas juga memotong penggunaan parameter posisi. Sekarang, jika Anda ingin membuat, katakanlah, serangkaian perangkat:

for Element in {7..${#IOStatOutput}} # Devices listed in elements 7 thru the last
do
    DevList+=( ${${=IOStatOutput[Element]}[1]} )
done

Saya menemukan bongkahan kecil lebih mudah untuk ditangani. Anda mungkin atau mungkin tidak perlu menggunakan referensi variabel tidak langsung, tergantung pada kode Anda. Mengetahui cara kerjanya masih merupakan hal yang baik untuk diketahui. Saya menggunakannya sendiri.

Friartek
sumber