Bagaimana cara mendapatkan nilai variabel jika nama variabel disimpan sebagai string?

142

Bagaimana saya bisa mengambil nilai variabel bash jika saya memiliki nama variabel sebagai string?

var1="this is the real value"
a="var1"
Do something to get value of var1 just using variable a.

Konteks:

Saya memiliki beberapa AMI ( Amazon Machine Image ) dan saya ingin menjalankan beberapa contoh dari setiap AMI. Segera setelah mereka selesai boot, saya ingin mengatur setiap instance sesuai dengan jenis AMI-nya. Saya tidak ingin memanggang banyak skrip atau kunci rahasia di dalam AMI apa pun, jadi saya menyiapkan skrip startup umum dan saya menaruhnya di S3 dengan tautan yang dapat diakses publik. Di rc.local saya meletakkan sepotong kecil kode yang mengambil skrip startup dan menjalankannya. Ini semua yang saya miliki di AMI. Kemudian setiap AMI mengakses skrip konfigurasi umum yang berlaku untuk semua AMI dan skrip pengaturan khusus untuk masing-masing. Skrip ini bersifat pribadi dan memerlukan URL yang ditandatangani untuk mengaksesnya.

Jadi sekarang, ketika saya memunculkan instance AMI (my_private_ami_1), saya meneruskan URL yang ditandatangani untuk satu file lagi yang disajikan pada S3 yang berisi URL yang ditandatangani untuk semua skrip pribadi dalam hal pasangan kunci / nilai.

config_url="http://s3.amazo.../config?signature"
my_private_ami_1="http://s3.amazo.../ami_1?signature"
...
Ketika skrip startup berjalan, itu mengunduh file di atas dan sourceitu. Kemudian ia memeriksa jenis AMI-nya dan memilih skrip pengaturan yang benar untuk dirinya sendiri.

ami\_type=GET AMI TYPE #ex: sets ami\_type to my\_private\_ami\_1
setup\_url=GET THE SETUP FILE URL BASED ON AMI\_TYPE # this is where this problem arises

Jadi sekarang saya dapat memiliki kode generik yang dapat mem-instance instans terlepas dari tipe AMI mereka dan instans dapat mengatur sendiri.

bhups
sumber

Jawaban:

257

Anda bisa menggunakan ${!a}:

var1="this is the real value"
a="var1"
echo "${!a}" # outputs 'this is the real value'

Ini adalah contoh ekspansi parameter tidak langsung :

Bentuk dasar ekspansi parameter adalah ${parameter}. Nilai parameterdiganti.

Jika karakter pertama dari parameteradalah tanda seru (!), Itu memperkenalkan tingkat tipuan variabel. Bash menggunakan nilai dari variabel yang dibentuk dari sisa parametersebagai nama variabel; variabel ini kemudian diperluas dan nilai itu digunakan di seluruh substitusi, bukan nilai parameteritu sendiri.

Phil Ross
sumber
4
Ini berfungsi untuk saya di bash OSX, tetapi tidak debian? Pada debian saya mendapatkan Bad substitutionerror.
23inhouse
11
@ 23inhouse Apakah Anda menjalankan skrip Anda menggunakan /bin/sh? Jika demikian, coba gunakan /bin/bashsebagai gantinya. Dari Debian Squeeze dan seterusnya, /bin/shdiubah menjadi symlink untuk dashalih - alihbash . dashtidak mendukung sintaksis khusus ini dan akan menghasilkan Bad substitutionkesalahan.
Phil Ross
8
Apakah ada cara untuk membuat ini berfungsi untuk nama variabel yang dimiliki array?
Loupax
Bekerja di ubuntu dengan bash
Sergey P. aka azure
1
@DoronShai Ini adalah contoh ekspansi parameter tidak langsung, yang didokumentasikan dalam Bash Reference Manual di gnu.org/software/bash/manual/html_node/…
Phil Ross
29
X=foo
Y=X
eval "Z=\$$Y"

set Z ke "foo"

Berhati-hatilah menggunakan evalkarena ini dapat memungkinkan pengecualian kode secara langsung melalui nilai-nilai di ${Y}. Ini dapat menyebabkan kerusakan melalui injeksi kode.

Sebagai contoh

Y="\`touch /tmp/eval-is-evil\`"

akan membuat /tmp/eval-is-evil. Ini juga bisa menjadi beberapa rm -rf /, tentu saja.

coba-tangkap-akhirnya
sumber
menyelamatkan ekor saya, ty
raffian
9

Memodifikasi kata kunci pencarian saya dan mendapatkannya :).

eval a=\$$a
Terima kasih atas waktunya.

bhups
sumber
Berhati-hatilah menggunakan eval karena hal ini memungkinkan pengecualian kode secara langsung melalui nilai dalam ${Y}. Lihat tambahan saya di jawaban pengguna "anon".
coba-tangkap-akhirnya
Ini adalah satu-satunya jawaban yang berhasil. Ironisnya, itu milik Anda sendiri!
Ctrl S
8

Untuk sesama pengguna zsh saya, cara untuk mencapai hal yang sama dengan jawaban yang diterima adalah dengan menggunakan:

${(P)a}

Ini disebut penggantian nama Parameter

Ini memaksa nilai nama parameter untuk ditafsirkan sebagai nama parameter lebih lanjut, yang nilainya akan digunakan jika perlu. Perhatikan bahwa flag yang diset dengan salah satu kelompok perintah typeset (khususnya transformasi kasus) tidak diterapkan pada nilai nama yang digunakan dalam mode ini.

Jika digunakan dengan parameter bersarang atau substitusi perintah, hasil yang akan diambil sebagai nama parameter dengan cara yang sama. Misalnya, jika Anda memiliki 'foo = bar' dan 'bar = baz', string $ {(P) foo}, $ {(P) $ {foo}}, dan $ {(P) $ (echo bar) } akan diperluas ke 'baz'.

Demikian juga, jika referensi itu sendiri bersarang, ekspresi dengan flag diperlakukan seolah-olah langsung diganti dengan nama parameter. Ini adalah kesalahan jika subtitusi bersarang ini menghasilkan array dengan lebih dari satu kata. Misalnya, jika 'name = assoc' di mana parameter assoc adalah array asosiatif, maka '$ {$ {(P) name} [elt]}' merujuk ke elemen asosiatif yang disubkripsikan 'elt'.

smac89
sumber
0

shell modern sudah mendukung array (dan bahkan array asosiatif). Jadi tolong gunakan mereka, dan gunakan eval lebih sedikit.

var1="this is the real value"
array=("$var1")
# or array[0]="$var1"

lalu ketika Anda ingin menyebutnya, echo $ {array [0]}

ghostdog74
sumber
apakah ini akan bekerja jika saya mendapatkan string dari argumen commandline (misalnya sebagai $1)?
jena
0

Berdasarkan jawaban: https://unix.stackexchange.com/a/111627

###############################################################################
# Summary: Returns the value of a variable given it's name as a string.
# Required Positional Argument: 
#   variable_name - The name of the variable to return the value of
# Returns: The value if variable exists; otherwise, empty string ("").
###############################################################################
get_value_of()
{
    variable_name=$1
    variable_value=""
    if set | grep -q "^$variable_name="; then
        eval variable_value="\$$variable_name"
    fi
    echo "$variable_value"
}

test=123
get_value_of test
# 123
test="\$(echo \"something nasty\")"
get_value_of test
# $(echo "something nasty")
VFein
sumber
0

Mengalami masalah yang sama dengan array, berikut adalah cara melakukannya jika Anda juga memanipulasi array:

array_name="ARRAY_NAME"
ARRAY_NAME=("Val0" "Val1" "Val2")

ARRAY=$array_name[@]
echo "ARRAY=${ARRAY}"
ARRAY=("${!ARRAY}")
echo "ARRAY=${ARRAY[@]}"
echo "ARRAY[0]=${ARRAY[0]}"
echo "ARRAY[1]=${ARRAY[1]}"
echo "ARRAY[2]=${ARRAY[2]}"

Ini akan menampilkan:

ARRAY=ARRAY_NAME[@]
ARRAY=Val0 Val1 Val2
ARRAY[0]=Val0
ARRAY[1]=Val1
ARRAY[2]=Val2
Alexandre Hamon
sumber