Dereferencja połączona nazwa zmiennej


12

Mogę to zrobić, ale wymaga to utworzenia ciągu zmiennej, a następnie wyrejestrowania go. Czy jest jakiś sposób, aby skrócić to do prostszego stwierdzenia?

#!/bin/bash

FRUITS="BANANA APPLE ORANGE"

BANANA_COLOUR="Yellow"
APPLE_COLOUR="Green or Red"
ORANGE_COLOUR="Blue"

for fruit in $( echo $FRUITS ); do
    fruit_colour="${fruit}_COLOUR"
    echo $fruit is ${!fruit_colour}
done

Próbowałem wiele rzeczy, jak ${!"${fruit}_COLOUR"}i \$${fruit}_COLOURwielu innych wariantów, ale jedynym sposobem, który pracował jest za pomocą łańcucha.


!Składnia jest flaga na zmiennej substytucji, że w zasadzie mówi „substytutem dwa razy”. Nie zmienia to wymogu, że to, co jest w środku, ${…}musi być nazwą zmiennej. Więc nie, nie możesz uniknąć używania zmiennej zawierającej nazwę, chyba że użyjesz innej metody ( eval).
Gilles „SO- przestań być zły”

Możesz także użyć tablic asocjacyjnych, aby zrobić to znacznie czystiej. Nie jest to jednak bezpośrednia odpowiedź na twoje pytanie, więc po prostu dodaj je jako komentarz.
Patrick

Odpowiedzi:


11

Po pierwsze, nie musisz używać $(echo $FRUITS)w forwyciągu. Wystarczy $FRUITSużyć. Następnie możesz pozbyć się jednej z linii wewnątrz pętli, używając eval.

evalPo prostu mówi bash, aby drugą ocenę z następującym stwierdzeniem (czyli o jeden więcej niż normalnej oceny). \$Przetrwa pierwszej oceny, jak $i następna ocena następnie traktuje to $jako początek nazwy zmiennej, która postanawia „Yellow”, etc.

W ten sposób nie musisz mieć osobnego kroku, który tworzy tymczasowy ciąg znaków (co moim zdaniem było głównym celem twojego pytania).

for fruit in $FRUITS ;do
    eval echo $fruit is \$${fruit}_COLOUR
done

W przypadku metody alternatywnej, o której wspomniał Patrick w komentarzu (powyżej), można zamiast tego użyć tablicy asocjacyjnej , w której indeks elementu nie musi być liczbą całkowitą. Możesz użyć łańcucha, takiego jak nazwa rodzaju owocu. Oto przykład z wykorzystaniem tablicy asocjacyjnej bash:

# This declares an associative array (It unsets it if it already exists)
declare -A colour
colour['BANANA']="Yellow"
colour["APPLE"]="Green or Red"
colour[MARTIAN ORANGE]="Blue"

for fruit in BANANA APPLE "MARTIAN ORANGE" ;do 
    echo "$fruit" is "${colour[$fruit]}"
done

6

Możesz użyć wbudowanego basha, evalaby to zrobić:

#!/bin/bash
FRUITS="BANANA APPLE ORANGE"
BANANA_COLOUR="Yellow"
APPLE_COLOUR="Green or Red"
ORANGE_COLOUR="Blue"

for fruit in $( echo $FRUITS );
do
    fruit_colour=${fruit}_COLOUR
    eval echo $fruit is \$${fruit_colour}
done

Zwróć uwagę na znak odwróconego dolara. Zasadniczo, „eval” przyczyny linia bash do podstawniki, na $fruiti ${fruit_color}, a następnie za pomocą evalzrobić drugą rundę substition przed wywołaniem echo.


1

Nie potrzebujesz ewaluacji. evaljest wskazówką w większości języków, że robisz coś złego.

foo_var=10
bar_var=12

# Build a string with your var name of choice
chosen_prefix='foo'
var_name="${chosen_prefix}_var"

# Use bang to deference it
chossen_value=${!var_name}
echo "Chossen value: ${chossen_value}"

Twierdzę, że jest to bezpieczniejsze w użyciu, evalponieważ przynajmniej ludzie wiedzą, że jest niebezpieczny, ${!var}jest tak samo niebezpieczny w bash(również podatność na iniekcję poleceń, jeśli $chosen_prefixjest pod kontrolą atakującego) i mniej osób jest tego świadomych. Spróbować var_name='a[$(uname>&2)0]' bash -c 'v=${!var_name}'. Najlepszym podejściem jest tutaj tablica asocjacyjna.
Stéphane Chazelas,
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.