Bash ve zsh'de çift ve üçlü ikame


23

Bu sorudaki arka plan kısmının takibi .

Gelen bashkullanabileceğim ${!FOO}içinde, çift ikame için zsh ${(P)FOO}. Her ikisinde de eski okul (hack-y) eval \$$FOOçalışıyor.

Yani, benim için en akıllı ve en mantıklı şey ${${FOO}}, ${${${FOO}}}…iki / üçlü / n ikame için olacaktır. Bu neden beklendiği gibi çalışmıyor?

İkincisi: Açıklamada ne \işe evalyarıyor? Bunun bir kaçış olduğunu, eval \$$$FOOimkansız gibi bir şey yaptığını düşünüyorum . Her kabuğunda işe yarayanlarla üçlü / n ikamesi nasıl yapılır?

Yanıtlar:


15

\Genişlemesini önlemek için kullanılmalıdır $$(cari işlem kimliği). Üçlü ikame için, çift değerlendirmeye ihtiyacınız vardır, bu yüzden her değerlendirmede istenmeyen açılımlardan kaçınmak için daha fazla kaçış:

#! /bin/bash
l0=value
l1=l0
l2=l1
l3=l2
l4=l3

echo $l0
eval echo \$$l1
eval eval echo \\$\$$l2
eval eval eval echo \\\\$\\$\$$l3
eval eval eval eval echo \\\\\\\\$\\\\$\\$\$$l4

Yine de: l3=l2; eval eval eval echo \\\$\\$\$$l353294tam olarak modüler değil.
Profpatsch

2
Neden böyle bir şey yapamıyorum eval $(eval echo \$$l2)? Bash'dan nefret ediyorum, hiçbir anlamı yok.
Profpatsch

@Profpatsch: Daha fazla örnek eklendi.
choroba

Ah, n². Neden böyle olduğunu anlamaya çalışmak bile istemiyorum.
Profpatsch

2
@ Profpatsch: Kolay. Her adımda, ters eğik çizgi sayısı yarıya (yani aslında 2ⁿ).
choroba


6

Değerinin FOOgeçerli bir değişken adı olduğunu varsayalım (say BAR), eval \$$FOOdeğerini BARayrı sözcüklere ayırır, her sözcüğü bir joker desen olarak görür ve sonucun ilk kelimesini komut olarak çalıştırır, diğer sözcükleri argüman olarak geçirir. Doların önündeki ters eğik çizgi kelimenin tam anlamıyla ele alınmasını sağlar, bu nedenle evalyerleşik yapıya iletilen argüman dört karakterli dizedir $BAR.

${${FOO}}bir sözdizimi hatası. “İkili ikame” yapmaz çünkü ortak mermilerin hiçbirinde böyle bir özellik yoktur (bu sözdiziminde değil). Zsh olarak, ${${FOO}} bir geçerli ve bir çift ikame, ama ne istiyorsunuz farklı davranır: Bu değerine iki ardışık dönüşümler gerçekleştiren FOO, kimlik dönüşümü, her ikisi de, bu nedenle yazının sadece süslü yolu ${FOO}.

Bir değişkenin değerini değişken olarak değerlendirmek istiyorsanız, doğru şekilde alıntı yapmakta dikkatli olun. Sonucu değişkene ayarlarsanız çok daha kolay:

eval "value=\${$FOO}"

1
Değişken olarak ayarlayarak ilk kelimenin komut olarak kullanılmaması için mi? Dostum, bu tür bir mantığı anlamak zor. Bu bash ile ilgili gibi görünen genel sorun.
Profpatsch

4

{ba,z}sh çözüm

İşte her ikisinde de çalışan bir işlev {ba,z}sh. POSIX uyumlu olduğuna da inanıyorum.

Alıntı yapmaktan çıldırmadan, bu gibi birçok yönlendirme düzeyi için kullanabilirsiniz:

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d

Veya daha fazla (!?!) İndirme seviyeniz varsa, bir döngü kullanabilirsiniz.

Verildiğinde uyarır:

  • Boş giriş
  • Ayarlanmamış bir değişken adı

# 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 [ -z "${1-}" ] || [ $# -ne 1 ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}

Benim için çalışan bir çapraz kabuk çözümü. Umarım "POSIX uyumlu çift değişkenli genişletme", gelecekte bu cevaba yönlendirir.
BlueDrink9

2

Tıpkı ${$foo}yerine çalışmaz ${(P)foo}içinde zshne yapar ${${$foo}}. Sadece her dolaylı seviyeyi belirtmeniz gerekir:

$ foo=bar
$ bar=baz
$ baz=3
$ echo $foo
bar
$ echo ${(P)foo}
baz
$ echo ${(P)${(P)foo}}
3

Elbette, ${!${!foo}}işe yaramadı bashçünkü bashiç içe geçmiş yer değiştirmelere izin vermiyor.


Teşekkürler, bunu bilmek güzel. Bunun sadece zsh olduğuna dair utanç verici, bu yüzden onu gerçekten bir senaryoya koyamazsınız (herkesin bash, ancak tersi değildir).
Profpatsch

Sanırım zshtahmin edebileceğinizden çok daha sık yüklü. Sık sık (ancak her zaman değil) olduğu shgibi bağlantılı olmayabilir bash, ancak kabuk komut dosyaları için hala kullanılabilir. Perl veya Python gibi düşün.
chepner

2

Bunu neden yapman gerekiyor?

Her zaman aşağıdaki gibi birkaç adımda yapabilirsiniz:

eval "l1=\${$var}"
eval "l2=\${$l1}"
...

Veya şöyle bir işlev kullanın:

deref() {
  if [ "$1" -le 0 ]; then
    eval "$3=\$2"
  else
    eval "deref $(($1 - 1)) \"\${$2}\" \"\$3\""
  fi
}

Sonra:

$ a=b b=c c=d d=e e=blah
$ deref 3 a res; echo "$res"
d
$ deref 5 a res; echo "$res"
blah

FWIW, içinde zsh:

$ echo ${(P)${(P)${(P)${(P)a}}}}
blah

Huh, birkaç adım kullanmak bana şu ana kadar gerçekleşmedi… garip.
Profpatsch

Benim açımdan, neden @Profpatsch'in bunu yapmak istediği açık . Çünkü bunu yapmanın en sezgisel yoludur. Bashta çoklu ikameleri uygulamak zor olmak başka bir şeydir.
Nikos Alexandris

2

POSIX çözümü

Alıntı yapmaktan çıldırmadan, bu işlevi aşağıdaki gibi birçok yönlendirme için kullanabilirsiniz:

$ a=b
$ b=c
$ c=d
$ echo $(var_expand $(var_expand $a)
d

Veya daha fazla (!?!) İndirme seviyeniz varsa, bir döngü kullanabilirsiniz.

Verildiğinde uyarır:

  • Boş giriş
  • Birden fazla argüman
  • Ayarlanmamış bir değişken adı

# Expand the variable named by $1 into its value. Works in both {ba,z}sh
# eg: a=HOME $(var_expand $a) == /home/me
function var_expand {
  if [ "$#" -ne 1 ] || [ -z "${1-}" ]; then
    printf 'var_expand: expected one argument\n' >&2;
    return 1;
  fi
  eval printf '%s' "\"\${$1?}\""
}

Bu cevabı önceki cevabınızla birleştirmemenizin bir nedeni var mı? Bir bakışta aralarındaki farkı göremiyorum.
BlueDrink9
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.