Co to jest ekspansja pośrednia? Co oznacza $ {! Var *}?

85

Czytam „ Bash Guide for Beginners ”. To mówi:

Jeśli pierwszy znak PARAMETERjest wykrzyknikiem, Bash używa wartości zmiennej utworzonej z reszty PARAMETERjako nazwy zmiennej; ta zmienna jest następnie interpretowana i ta wartość jest używana w pozostałej części podstawiania, a nie jako wartość PARAMETERsama w sobie. Jest to znane jako ekspansja pośrednia.

Podany przykład to:

franky ~> echo ${!N*}
NNTPPORT NNTPSERVER NPX_PLUGIN_PATH

Nie całkiem rozumiem tutaj:

wartość zmiennej utworzonej z reszty PARAMETER

Ponieważ PARAMETERto właśnie !N*, a następnie

reszta PARAMETER

jest po prostu N*. Jak to może tworzyć zmienną? Czy Bash przeszukał tam wszystkie możliwe polecenia?

Athos
źródło

Odpowiedzi:

110

Jeśli przeczytasz bashstronę podręcznika , w zasadzie potwierdza to, co powiedziałeś:

Jeśli pierwszym znakiem parametru jest wykrzyknik ( !), wprowadzany jest poziom pośredniej zmiennej. Bash używa wartości zmiennej utworzonej z reszty parametru jako nazwy zmiennej; ta zmienna jest następnie interpretowana i ta wartość jest używana w pozostałej części podstawiania zamiast wartości samego parametru. Jest to znane jako ekspansja pośrednia.

Jednak czytając dalej:

Wyjątkami od tej reguły są rozszerzenia ${!prefix*}i ${!name[@]}opisane poniżej.

${!prefix*}Nazwy pasujące do prefiksu. Interpretowane jako nazwy zmiennych, których nazwy rozpoczynają się od przedrostka, oddzielone pierwszym znakiem IFSzmiennej specjalnej.

Innymi słowy, Twój konkretny przykład ${!N*}jest wyjątkiem od zacytowanej reguły. To jest jednak praca jako reklamowane w oczekiwanych przypadkach, takich jak:

$ export xyzzy=plugh ; export plugh=cave

$ echo ${xyzzy}  # normal, xyzzy to plugh
plugh

$ echo ${!xyzzy} # indirection, xyzzy to plugh to cave
cave
paxdiablo
źródło
1
Dziękuję za odpowiedź. Im więcej czytam „Bash guide for Beginners”, tym bardziej zadaję sobie pytanie, czy autorka rozumie, co pisze.
LRDPRDX
23

Wydaje się, że istnieje wyjątek, gdy dane „pośrednie” kończy się na a *, tak jak ma to miejsce w tym przypadku. W tym przypadku podaje wszystkie nazwy zmiennych zaczynające się od określonej części ( Ntutaj). Bash może to zrobić, ponieważ śledzi zmienne i wie, które z nich istnieją.

Prawdziwe pośrednie jest to:
powiedzmy, że mam $VARIABLEustawioną zmienną 42i mam inną zmienną $NAMEustawioną na VARIABLE. ${!NAME}da mi 42. Używasz wartości jednej zmiennej, aby podać nazwę innej:

$ NAME="VARIABLE"
$ VARIABLE=42
$ echo ${!NAME}
42
Kevin
źródło
5
Wow, kto wiedział, że tak łatwo jest uzyskać odpowiedź na temat sensu życia, wszechświata i wszystkiego!
KomodoDave
3

Tak, wyszukuje wszystkie możliwe rozszerzenia zmiennych po!. Jeśli zrobiłeś:

echo ${!NP*}

dostaniesz tylko NPX_PLUGIN_PATH.

Rozważmy następujący przykład:

:~> export myVar="hi"
:~> echo ${!my*}
    myVar
:~> export ${!my*}="bye"
:~> echo $myVar
    bye
tpg2114
źródło
czy inne zmienne, które pasują do mojego *, również zostaną ustawione na „pa”?
Anthony
1
@Anthony Wypróbowałem to i jeśli ${!my*}rozszerza się do myA, myB, myA jest eksportowane ze swoją bieżącą wartością, a myB jest ustawione na „bye” i wyeksportowane. Niezbyt przydatne.
GKFX
3

Trafiłeś w wyjątek w przetwarzaniu pośrednim, gdzie jeśli ostatnim znakiem jest *, zwrócone zostaną wszystkie zmienne, które mają wcześniej podany prefiks.

Ignacio Vazquez-Abrams
źródło
Więc poza *przypadkiem, czy to to samo co ${${VAR}}?
chronospoon
1
@chronospoon,, w skrócie z ${${VAR}}możliwością zapisu, ponieważ ${$VAR}nie jest legalne, ponieważ $VARzwraca ciąg znaków, który nie może występować po $znaku; aby użyć łańcucha jako nazwy zmiennej, musisz wprowadzić jeden poziom pośredni (jak podano w samym oryginalnym pytaniu), tj. możesz użyć ${!VAR}, który robi dokładnie to, czego się spodziewałeś (błędnie, ale zrozumiale) ${$VAR}.
Enrico,