Nie mam pojęcia o skrypcie bash.
Mam następujący kod:
function grep_search() {
magic_way_to_define_magic_variable_$1=`ls | tail -1`
echo $magic_variable_$1
}
Chcę mieć możliwość stworzenia nazwy zmiennej zawierającej pierwszy argument polecenia i noszącej wartość np. Ostatniej linii ls
.
Aby zilustrować, czego chcę:
$ ls | tail -1
stack-overflow.txt
$ grep_search() open_box
stack-overflow.txt
Jak więc mam zdefiniować / zadeklarować $magic_way_to_define_magic_variable_$1
i jak mam to nazwać w skrypcie?
Próbowałem eval
, ${...}
, \$${...}
, ale nadal jestem zdezorientowany.
Odpowiedzi:
Użyj tablicy asocjacyjnej z nazwami poleceń jako kluczami.
Jeśli nie możesz używać tablic asocjacyjnych (np. Musisz obsługiwać
bash
3), możesz użyćdeclare
do tworzenia dynamicznych nazw zmiennych:i użyj pośredniego rozwijania parametrów, aby uzyskać dostęp do wartości.
Zobacz BashFAQ: Indirection - Ocenianie zmiennych pośrednich / odniesienia .
źródło
-a
deklaruje tablicę indeksowaną, a nie tablicę asocjacyjną. O ile argument do niegrep_search
jest liczbą, będzie traktowany jako parametr z wartością liczbową (która domyślnie wynosi 0, jeśli parametr nie jest ustawiony).4.2.45(2)
i deklaruję, że nie wymienia go jako opcjideclare: usage: declare [-afFirtx] [-p] [name[=value] ...]
. Wydaje się jednak, że działa poprawnie.declare -h
w 4.2.45 (2) dla mnie pokazujedeclare: usage: declare [-aAfFgilrtux] [-p] [name[=value] ...]
. Możesz dwukrotnie sprawdzić, czy faktycznie używasz 4.x, a nie 3.2.declare $varname="foo"
?${!varname}
jest znacznie prostszy i szeroko kompatybilnyOstatnio szukałem lepszego sposobu na zrobienie tego. Tablica asocjacyjna brzmiała dla mnie jak przesada. Zobacz co znalazłem:
...i wtedy...
źródło
prefix_${middle}_postfix
(tzn. formatowanie nie będzie działaćvarname=$prefix_suffix
)Oprócz tablic asocjacyjnych istnieje kilka sposobów uzyskiwania zmiennych dynamicznych w Bash. Zwróć uwagę, że wszystkie te techniki stwarzają ryzyko, które omówiono na końcu tej odpowiedzi.
W poniższych przykładach założę,
i=37
że chcesz utworzyć alias zmiennej o nazwie,var_37
której wartość początkowa tololilol
.Metoda 1. Wykorzystanie zmiennej „wskaźnikowej”
Możesz po prostu zapisać nazwę zmiennej w zmiennej pośredniej, podobnie jak wskaźnik C. Bash ma wtedy składnię do odczytywania zmiennej z aliasem:
${!name}
rozwija się do wartości zmiennej, której nazwa jest wartością zmiennejname
. Można to potraktować jako dwustopniowe rozszerzenie:${!name}
rozszerza się do$var_37
, które rozszerza się dololilol
.Niestety, nie ma odpowiednika składni do modyfikowania zmiennej z aliasem. Zamiast tego możesz wykonać zadanie za pomocą jednej z poniższych sztuczek.
1a. Przypisywanie z
eval
eval
jest zła, ale jest też najprostszym i najbardziej przenośnym sposobem osiągnięcia naszego celu. Musisz ostrożnie uciec z prawej strony zadania, ponieważ zostanie ono ocenione dwukrotnie . Łatwym i systematycznym sposobem na zrobienie tego jest uprzednia ocena prawej strony (lub użycieprintf %q
).I powinieneś sprawdzić ręcznie, czy lewa strona jest poprawną nazwą zmiennej lub nazwą z indeksem (a co jeśli był
evil_code #
?). Z kolei wszystkie inne poniższe metody wymuszają to automatycznie.Wady:
eval
jest zły.eval
jest zły.eval
jest zły.1b. Przypisywanie z
read
read
Wbudowane pozwala na przypisanie wartości do zmiennej której podano nazwisko, fakt, który można wykorzystać w połączeniu z tu-strings:IFS
Części i opcji-r
upewnij się, że wartość jest przypisana jak jest natomiast opcja-d ''
pozwala na przypisanie wartości obsługujący kilka linii. Z powodu tej ostatniej opcji polecenie wraca z niezerowym kodem zakończenia.Zauważ, że ponieważ używamy ciągu tutaj, do wartości dodawany jest znak nowego wiersza.
Wady:
1c. Przypisywanie z
printf
Od wersji Bash 3.1 (wydanej w 2005 r.),
printf
Funkcja wbudowana może również przypisać wynik do zmiennej, której nazwa została podana. W przeciwieństwie do poprzednich rozwiązań, po prostu działa, nie jest potrzebny dodatkowy wysiłek, aby uciec przed rzeczami, aby zapobiec pękaniu i tak dalej.Wady:
Metoda 2. Wykorzystanie zmiennej „referencyjnej”
Od wersji Bash 4.3 (wydanej w 2014 r.),
declare
Wbudowane oprogramowanie ma opcję-n
tworzenia zmiennej, która jest „odniesieniem do nazwy” innej zmiennej, podobnie jak odwołania w C ++. Podobnie jak w metodzie 1, odwołanie przechowuje nazwę zmiennej z aliasem, ale za każdym razem, gdy uzyskuje się dostęp do odwołania (w celu odczytu lub przypisania), Bash automatycznie rozwiązuje problem pośredni.Ponadto Bash ma szczególny i składnię bardzo mylące dla uzyskania wartości samego odniesienia, sędzia przez siebie:
${!ref}
.Nie pozwala to uniknąć pułapek opisanych poniżej, ale przynajmniej sprawia, że składnia jest prosta.
Wady:
Ryzyka
Wszystkie te techniki aliasingu stwarzają kilka zagrożeń. Pierwszym z nich jest wykonanie dowolnego kodu za każdym razem, gdy rozwiązujesz problem pośredni (do odczytu lub do przypisania) . Rzeczywiście, zamiast nazwy zmiennej skalarnej, na przykład
var_37
, równie dobrze możesz aliasować indeks tablicy, na przykładarr[42]
. Ale Bash ocenia zawartość nawiasów kwadratowych za każdym razem, gdy jest to potrzebne, więc aliasowaniearr[$(do_evil)]
będzie miało nieoczekiwane efekty… W konsekwencji, używaj tych technik tylko wtedy, gdy kontrolujesz pochodzenie aliasu .Drugie ryzyko to tworzenie aliasu cyklicznego. Ponieważ zmienne Bash są identyfikowane na podstawie nazwy, a nie zakresu, możesz nieumyślnie utworzyć alias dla siebie (myśląc, że utworzy alias zmiennej z otaczającego zakresu). Może się to zdarzyć w szczególności w przypadku używania wspólnych nazw zmiennych (takich jak
var
). W konsekwencji tych technik należy używać tylko wtedy, gdy kontrolujesz nazwę zmiennej z aliasem .Źródło:
źródło
${!varname}
technika wymaga użycia zmiennej pośredniejvarname
.Poniższy przykład zwraca wartość $ name_of_var
źródło
echo
s z podstawieniem polecenia (bez cudzysłowów) jest niepotrzebne. Dodatkowo-n
należy dać opcjęecho
. I jak zawszeeval
jest niebezpieczne. Ale to wszystko jest niepotrzebne, ponieważ Bash ma bezpieczniejszego, bardziej przejrzystą i krótszą składnię tego samego celu:${!var}
.To powinno działać:
źródło
To też zadziała
W Twoim przypadku
źródło
Zgodnie BashFAQ / 006 , można skorzystać
read
z tutaj składni strun do przypisywania zmiennych pośrednich:Stosowanie:
źródło
Posługiwać się
declare
Nie ma potrzeby używania przedrostków, jak w przypadku innych odpowiedzi, ani tablic. Używaj tylko
declare
, podwójnych cudzysłowów i rozwijania parametrów .Często używam następującej sztuczki do analizowania list
one to n
argumentów zawierających argumenty sformatowane w następujący sposóbkey=value otherkey=othervalue etc=etc
:Ale rozszerzenie listy argv, np
Dodatkowe wskazówki
źródło
printf
lubeval
Wow, większość składni jest okropna! Oto jedno rozwiązanie z prostszą składnią, jeśli potrzebujesz pośrednio odwoływać się do tablic:
W przypadku prostszych przypadków użycia zalecam składnię opisaną w Przewodniku po zaawansowanych skryptach Bash .
źródło
foo_1
ifoo_2
są wolne od spacji i znaków specjalnych. Przykłady problematycznych wpisów:'a b'
utworzy dwa wpisy w środkumine
.''
nie utworzy wejścia do środkamine
.'*'
rozwinie się do zawartości katalogu roboczego. Możesz zapobiec tym problemom, cytując:eval 'mine=( "${foo_'"$i"'[@]}" )'
[@]
konstrukcje."${array[@]}"
zawsze rozwinie się do właściwej listy wpisów bez problemów, takich jak dzielenie słów lub rozwijanie*
. Ponadto problem dzielenia słów można obejść tylkoIFS
wtedy, gdy znasz dowolny znak inny niż pusty, który nigdy nie pojawia się w tablicy. Co więcej, dosłowne traktowanie*
nie może zostać osiągnięte poprzez ustawienieIFS
. Albo ustawiszIFS='*'
i rozdzielisz się na gwiazdach, albo ustawiszIFS=somethingOther
i*
rozszerzysz.|
lubLF
jako IFS. Ponownie, ogólny problem w pętlach polega na tym, że tokenizacja zachodzi domyślnie, więc cudzysłowy jest specjalnym rozwiązaniem umożliwiającym rozszerzone ciągi zawierające tokeny. (Jest to albo rozwijanie globów / parametrów, albo ciągi rozszerzone w cudzysłowach, ale nie oba.) Jeśli do odczytania zmiennej potrzeba 8 cudzysłowów, to powłoka jest niewłaściwym językiem.W przypadku tablic indeksowanych możesz odwoływać się do nich w następujący sposób:
Do tablic asocjacyjnych można odwoływać się w podobny sposób, ale zamiast tego trzeba
-A
włączyć przełącznik .declare
-a
źródło
Dodatkową metodą, która nie zależy od posiadanej wersji powłoki / bash, jest użycie
envsubst
. Na przykład:źródło
script.sh
plik:Test:
Jak na
help eval
:Możesz również użyć
${!var}
rozwijania pośredniego Bash , jak już wspomniano, jednak nie obsługuje pobierania indeksów tablic.Aby uzyskać więcej informacji lub przykłady, sprawdź BashFAQ / 006 o Indirection .
Należy jednak ponownie rozważyć użycie pośredniej, zgodnie z następującymi uwagami.
źródło
do
varname=$prefix_suffix
formatu wystarczy użyć:źródło