$ {! FOO} dan zsh

13

${!FOO}melakukan substitusi ganda dalam bash, yang berarti mengambil nilai (string) FOO dan menggunakannya sebagai nama variabel.
zshtidak mendukung fitur ini.

Apakah ada cara untuk membuat ini bekerja sama di bashdan zsh?

Latar Belakang:

Saya punya daftar variabel lingkungan, seperti

PATH MAIL EDITOR

dan ingin mencetak nama variabel terlebih dahulu dan kemudian nilainya.

Ini berfungsi bashtetapi tidak zsh:

for VAR in LIST
do
        echo $VAR
        echo ${!VAR}
done

Entah bagaimana itu mungkin menjadi "jalan lama" dengan eval, tetapi saya tidak bisa membuatnya bekerja:

for VAR in LIST
do
        echo $VAR
        echo `eval \$$VAR`
done

Saya tidak akan pernah mengerti mengapa saya tidak bisa begitu saja melakukan substitusi mendalam seperti ${${VAR}}atau bahkan ${${${VAR}}}jika perlu, jadi penjelasan untuk itu juga bagus.

Profpatsch
sumber
1
HAH! Saya pikir zsh seharusnya memiliki lebih banyak fitur.
Ярослав Рахматуллин
4
Tidak perlu sesumbar dengannya bash, ia memang memiliki fungsi yang sama (dan lebih banyak lagi), hanya menggunakan pola yang berbeda, yaitu ${(p)FOO}.
Profpatsch
2
@ ЯрославРахматуллин, (e)flag ekspansi parameter zsh telah ditambahkan di zsh-2.6-beta17 (Mei 1996), (P)flag dalam 3.1.5-pws-7 (1999). bash ada ${!var}di 2.0 (Desember 1996).
Stéphane Chazelas

Jawaban:

18

Baik bash dan zsh memiliki cara untuk melakukan ekspansi tidak langsung, tetapi mereka menggunakan sintaks yang berbeda.

Cukup mudah untuk melakukan ekspansi tidak langsung menggunakan eval; ini bekerja di semua shell POSIX dan sebagian besar Bourne. Berhati-hatilah untuk mengutip dengan benar jika nilainya mengandung karakter yang memiliki arti khusus dalam shell.

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

${${VAR}}tidak berfungsi karena itu bukan fitur yang diterapkan shell. Hal di dalam kurung harus sesuai dengan aturan sintaksis yang tidak termasuk ${VAR}. (Dalam zsh, ini adalah sintaks yang didukung, tetapi melakukan sesuatu yang berbeda: substitusi bersarang melakukan transformasi berturut-turut pada nilai yang sama; ${${VAR}}sama dengan $VARkarena ini melakukan transformasi identitas dua kali pada nilainya.)

Gilles 'SANGAT berhenti menjadi jahat'
sumber
19

Komentar, di bawah pertanyaan awal, oleh Profpatsch memberikan contoh ${(p)FOO}untuk menampilkan konten dari referensi variabel tidak langsung. Bendera salah atau salah ketik, itu harus huruf kapital P dan bukan huruf kecil p. Gunakan: ${(P)FOO}.

Berikut ini harus menghasilkan output yang diinginkan:

#! /bin/zsh
LIST=(PATH MAIL EDITOR)
for VAR in ${LIST}
do
    print "${VAR}:  ${(P)VAR}"
done

Dari halaman manual zshexpn ; bagian - Bendera Ekspansi Parameter :

P

  This forces the value of the parameter name to be interpreted as a further
  parameter name,  whose  value  will  be used where appropriate.  Note that
  flags set with one of the typeset family of commands (in particular case
  transformations) are not applied to the value of name used in this fashion.

  If used with a nested parameter or command substitution, the result of that
  will be taken as a parameter  name  in the  same  way.   For  example,  if
  you  have  `foo=bar'  and `bar=baz', the strings ${(P)foo}, ${(P)${foo}}, and
  ${(P)$(echo bar)} will be expanded to `baz'

Pada suatu waktu saya membaca mengapa ${${${VAR}}}tidak menghasilkan output yang Anda harapkan, tetapi saat ini saya tidak dapat menemukannya. Anda dapat melakukan sesuatu seperti berikut ini:

first="second" ; second="third" ; third="fourth" ; fourth="fifth"
print ${(P)${(P)${(P)first}}}
fifth
Friartek
sumber
3

Anda tidak menggunakan eval dengan benar. Dalam nilai contoh Anda $ VAR diawali dengan "$" (yaitu `$ VALUE ') akan dieksekusi sebagai perintah. Bukan itu yang Anda inginkan. Anda ingin mengevaluasi perluasan variabel yang namanya diambil dari variabel lain.

$ for i in `echo PATH MAIL EDITOR`; 
    do eval moo="\${$i}" 
    echo $moo 
done
/usr/local/sbin:/usr/local/bin:/usr/sbin:/u (...)
/var/mail/root
nano
Ярослав Рахматуллин
sumber
0

{ba,z}sh larutan

Inilah fungsi yang bekerja di {ba, z} sh. Saya percaya itu juga sesuai dengan POSIX.

Ini memperingatkan ketika diberikan:

  • Masukan kosong
  • Lebih dari satu argumen
  • Nama variabel yang tidak disetel

# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
var_expand() {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one non-empty argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}
Tom Hale
sumber
Tidak ada functionatau tidak [[...]]dalam POSIX sh.
Stéphane Chazelas
Cek untuk tidak ada argumen harus dengan [ "$#" -eq 0 ](atau mungkin [ "$#" -ne 1 ]karena Anda hanya mengharapkan satu argumen).
Stéphane Chazelas
@ StéphaneChazelas Terima kasih, diperbarui. Apakah ada shell referensi untuk kepatuhan POSIX? Saya pikir shell-checkmungkin membantu juga.
Tom Hale
@ StéphaneChazelas Re tes, saya juga ingin ingin gagal ketika diberi argumen nol tunggal. Saya telah menambahkan saran ke-2 Anda. Bersulang!
Tom Hale
Saya setuju dengan penawaran yang ${1-}diberikan a='"" -o -n 1' [ -z $a ]== 0. Tetapi tidak akan $#selalu menjadi token tunggal?
Tom Hale