Apakah mungkin untuk mencetak konten konten variabel dengan skrip shell? (referensi tidak langsung)

13

Misalkan saya telah mendeklarasikan variabel berikut:

$ var='$test'
$ test="my string"

Jika saya mencetak isinya, saya melihat yang berikut:

$ echo $var
$test

$ echo $test
my string

Saya ingin mencari cara untuk mencetak konten dari konten $var(yang merupakan konten $test). Jadi saya mencoba melakukan hal berikut:

$ echo $(echo $var)
$test

Tapi di sini hasilnya $testdan tidak "my string"... Apakah mungkin untuk mencetak konten dari konten variabel menggunakan bash?

Rafael Muynarsk
sumber
Bisakah Anda mengedit posting Anda untuk mengatakan shell mana yang Anda gunakan, atau persyaratan portabilitas apa yang Anda miliki?
Michael Homer
@MichaelHomer Tentu, tapi bagaimana tepatnya saya bisa memeriksa informasi ini?
Rafael Muynarsk
Ini lebih dari sesuatu yang Anda pilih daripada sesuatu untuk diperiksa. Misalnya, apakah Anda menjalankan skrip dengan ash, atau bash, cshatau dash, ..., atau zsh, atau POSIX sh, atau apakah Anda perlu bekerja di berbagai sistem?
Michael Homer
@MichaelHomer Ah, saya mengerti ... Saya menggunakan bash. Saya hanya akan mengubah pertanyaan terakhir dan menyisipkan tag baru.
Rafael Muynarsk

Jawaban:

21

Anda dapat melakukannya dengan menggunakan ekspansi variabel tidak langsung bash (selama Anda boleh $mengabaikan variabel referensi Anda):

$ var=test
$ test="my string"
$ echo "$var"
test
$ echo "${!var}"
my string

3.5.3 Ekspansi Parameter Shell

jesse_b
sumber
10

Untuk kasus ketika nama variabel yang terkandung vardiawali dengan $Anda dapat menggunakan eval:

$ var='$test'
$ test="my string"
$ eval echo $var
my string

Apa yang terjadi di sini:

  • bash memperluas $varke nilai $test, menghasilkan eval echo $testperintah;
  • evalmengevaluasi echo $testekspresi dan menghasilkan hasil yang diinginkan.

Perhatikan, bahwa menggunakan evalsecara umum mungkin berbahaya (tergantung pada apa yang disimpan var), jadi lebih baik menghindarinya. Fitur ekspansi tidak langsung lebih baik untuk kasus Anda (tetapi Anda harus menyingkirkan proses $masuk $test).

Danila Kiver
sumber
Tentu, itu salah ketik. Posting asli memiliki kutipan tunggal. Tetap.
Danila Kiver
4
Saya selalu merekomendasikan referensi variabel kutip ganda (untuk menghindari masalah dari pemisahan kata yang tidak terduga dan ekspansi wildcard). Dengan eval, Anda memerlukan dua lapis tanda kutip ganda, garis ini: eval echo "\"$var\"". Pikiran Anda, ini tidak melakukan apa-apa tentang bahaya lain menggunakan evalyang Anda sebutkan.
Gordon Davisson
9

Mirip dengan jawaban Jesse_b , tetapi menggunakan variabel referensi nama alih-alih variabel tidak langsung (memerlukan bash4.3+):

$ declare -n var=test
$ test="my string"
$ echo "$var"
my string

Variabel referensi nama varmenyimpan nama variabel yang dirujuknya. Ketika variabel dereferenced sebagai $var, nilai variabel lain dikembalikan.

bash selesaikan referensi nama secara rekursif:

$ declare -n var1=var2
$ declare -n var2=test
$ test="hello world"
$ echo "$var1"
hello world

Untuk kelengkapan, menggunakan array asosiatif (dalam bash4.0+) juga merupakan salah satu cara untuk menyelesaikannya, tergantung pada persyaratan:

$ declare -A strings
$ strings[test]="my string"
$ var=test
$ echo "${strings[$var]}"
my string

Ini memberikan cara yang lebih fleksibel untuk mengakses lebih dari satu nilai dengan kunci atau nama yang dapat ditentukan secara dinamis. Ini mungkin lebih baik jika Anda ingin mengumpulkan semua nilai dari kategori tertentu dalam satu array, tetapi masih dapat mengaksesnya dengan beberapa kunci (misalnya nama yang dapat diakses dengan ID, atau nama jalur yang dapat diakses dengan tujuan dll) karena tidak mencemari namespace variabel dari skrip.

Kusalananda
sumber
3

Dengan zsh:

$ var='$test'
$ test='*'
$ printf '%s\n' ${(e)var}
*

Dengan kulit seperti Bourne

$ eval 'printf "%s\n" "'"$var"'"'
*

Ingat bahwa dalam ekspansi variabel shells harus dikutip untuk mencegah split + glob ( zshmenjadi pengecualian) dan echotidak dapat digunakan untuk data sewenang-wenang.

Karena dengan kedua (e)dan evalada evaluasi kode shell, penting konten $vartinggal di dalam kendali Anda sebagai sebaliknya yang dapat menjadi kerentanan injeksi perintah sewenang-wenang (sama dengan ${!var}pendekatan).

Stéphane Chazelas
sumber