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.