Użyj odniesienia do zmiennej „wewnątrz” innej zmiennej

27

Jestem pewien, że jest to stosunkowo proste, po prostu nie wiem jak to zrobić.

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

Chcę coś takiego, echo ${$myvar}co chcę interpretować jako ${$myvar}-> ${6}->value

Brandon Kreisel
źródło
4
Terminem technicznym jest zmienna pośrednia .
Thor

Odpowiedzi:

29

Możesz to zrobić za pomocą evalwbudowanego w wiele dobrych powłok, w tym ksh:

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

Sztuczka polega na podwójnym zacytowaniu łańcucha, którym karmisz, aby eval$ myvar został zastąpiony przez „6”, i odwrotnym ukośnikiem na zewnętrznym znaku dolara, aby evaluzyskać ciąg „6 $”.

Mam dane wyjściowe „% user”, ale wypróbowałem to na wieloprocesorowym komputerze RHEL.

Bruce Ediger
źródło
4
Jesteś oficjalnie Najwyższym Wielkim Mistrzem tygodnia b / c, który działa nawet na niesamowicie okropnym ksh (naprawdę pdksh) w OpenBSD 5.4. Jeśli chcesz ustawić var vv na wartość var, którego nazwa znajduje się w var vn , po prostu zrób vv=$( eval "echo \$$vn" ). Wielkie dzięki!
execNext
25

Pośrednie odniesienie do zmiennej

Nowoczesne zaawansowane powłoki mają metodę odwoływania się do wartości zmiennej, której nazwa jest przechowywana w innej zmiennej. Niestety metoda różni się między ksh, bash i zsh.

W mksh ≥R39b możesz utworzyć myvarnazwę:

typeset -n myvar=6
echo "$myvar"

To nie działa w ATT ksh93, ponieważ nie obsługuje nazw w parametrach pozycyjnych. W przypadku, gdy masz zmienną zawierającą nazwę zmiennej, możesz użyć tej metody.

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

W bash ≥2,0 możesz pisać

echo "${!myvar}"

W Zsh możesz pisać

echo ${(P)myvar}

W starszych powłokach, w tym ksh88 i pdksh, jedyne wyjście, gdy masz zmienną zawierającą inną nazwę zmiennej i chcesz użyć wartości tej zmiennej eval, jak wyjaśnił Bruce Ediger . To rozwiązanie działa w dowolnej powłoce Bourne / POSIX.

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

Korzystanie z tablicy

To najlepsza metoda tutaj: jest prostsza i bardziej przenośna.

W twoim przypadku użycia, w dowolnej powłoce z tablicami (wszystkie warianty ksh, bash ≥2.0, zsh), możesz przypisać zmienną tablicową i wziąć żądany element. Uwaga: tablice ksh i bash zaczynają numerację od 0, ale zsh zaczyna się od 1, chyba że wydasz setopt ksh_arrayslub emulate ksh.

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

Jeśli chcesz skopiować parametry pozycyjne do zmiennej tablicowej a:

set -A a -- "$@"

W ksh93, mksh ≥R39b, bash ≥2,0 i zsh, możesz użyć składni przypisania tablicy:

iostat=($(iostat))
echo "${iostat[5]}"
Gilles „SO- przestań być zły”
źródło
Wow, twoje rozwiązanie „Bourne / POSIX” działa również w ksh / pdksh w OpenBSD 5.4. Aby zastosować go do przykładu w moim komentarzu do powyższej odpowiedzi Bruce'a Edigera, po prostu zrób eval "vv=\${$vn}". Merci beaucoup, miły panie.
execNext
1

Jak wskazał Gilles (który udzielił bashczęści odpowiedzi), również nie unieważniając Bruce'a Edigera (jak to zrobić przenośnie eval), oto jak to zrobić namerefw ostatnim mksh(i AT&T ksh93, z wyjątkiem - jak skomentował @Gilles - namerefs nie może odnosić się do parametrów pozycyjnych w AT&T ksh, tylko do nazwanych parametrów):

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

Dodano także opcję „ --po” setdla zwiększenia odporności.

mirabilos
źródło
Począwszy od ksh 93u, nazwy nazw nie mogą odwoływać się do parametrów pozycyjnych ( typeset: 6: invalid variable name).
Gilles „SO- przestań być zły”
0

Kolejne użycie tablic

Od jakiegoś czasu nie używałem ani ksh, ani żadnego wariantu, więc nie jestem pewien, czy ksh (lub bash) ma podobne możliwości. Moją podstawową powłoką jest zsh. Używam tablic do obsługi danych wyjściowych z poleceń takich jak iostat, ponieważ tworzą one wiele linii, a nie wszystkie linie mają ten sam format / długość.

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

Powyższe pomija również użycie parametrów pozycyjnych. Teraz, jeśli chcesz wygenerować, powiedzmy, szereg urządzeń:

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

Mniejsze fragmenty są o wiele łatwiejsze w obsłudze. W zależności od kodu może być konieczne użycie odwołania do zmiennej pośredniej. Dobrze wiedzieć, jak to działa. Sam go używam.

Friartek
źródło