Name der verketteten Variablen für die Differenz

12

Ich kann dies tun, aber es erfordert, dass eine Zeichenfolge der Variablen erstellt und dann dereferenziert wird. Gibt es eine Möglichkeit, es auf eine einfachere Aussage zu verkürzen?

#!/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

Ich habe viele Dinge wie ${!"${fruit}_COLOUR"}oder \$${fruit}_COLOURund viele andere Varianten ausprobiert , aber der einzige Weg, der funktioniert hat, ist die Verwendung eines Strings.

Matte
quelle
Die !Syntax ist ein Flag für die Variablensubstitution, das im Grunde "zweimal ersetzen" sagt. Es ändert nichts an der Anforderung, dass das, was sich im ${…}befindet, ein Variablenname sein muss. Nein, Sie können es nicht vermeiden, eine Variable zu verwenden, die den Namen enthält, es sei denn, Sie verwenden eine andere Methode ( eval).
Gilles 'SO- hör auf böse zu sein'
Sie können auch assoziative Arrays verwenden, um dies viel sauberer zu machen. Es ist jedoch keine direkte Antwort auf Ihre Frage. Fügen Sie sie einfach als Kommentar hinzu.
Patrick

Antworten:

10

Zunächst müssen Sie nicht $(echo $FRUITS)in der forAnweisung verwenden. Nur zu benutzen $FRUITSist genug. Dann können Sie mit eine der Linien in der Schleife entfernen eval.

Das evalfordert bash einfach auf, eine zweite Bewertung der folgenden Aussage vorzunehmen (dh eine mehr als die normale Bewertung). Der \$überlebt die erste Auswertung als $, und die nächste Auswertung behandelt dies dann $als den Beginn eines Variablennamens, der in "Gelb" usw. aufgelöst wird.

Auf diese Weise brauchen Sie keinen separaten Schritt, der eine Zwischenzeichenfolge erstellt (was meiner Meinung nach die Hauptabsicht Ihrer Frage war).

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

Für eine alternative Methode, wie von Patrick in einem Kommentar (oben) erwähnt, können Sie stattdessen ein assoziatives Array verwenden , in dem der Index eines Elements keine Ganzzahl sein muss. Sie können eine Zeichenfolge verwenden, z. B. den Namen einer Obstsorte. Hier ist ein Beispiel mit dem assoziativen Array von 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
Peter.O
quelle
6

Sie können die Bash-Builtin verwenden, evalum dies zu tun:

#!/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

Beachten Sie das umgekehrte Dollarzeichen. Grundsätzlich wird durch die "eval" -Linie bash ersetzt $fruitund ${fruit_color}anschließend evaleine zweite Runde der Substitution durchgeführt, bevor der Anruf ausgeführt wird echo.

Bruce Ediger
quelle
1

Sie brauchen keine Bewertung. evalist in den meisten Sprachen ein Hinweis darauf, dass Sie etwas falsch machen.

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}"
Joseph Lust
quelle
Ich würde behaupten, dass es sicherer ist, es zu verwenden, evalda zumindest die Leute wissen, dass es gefährlich ${!var}ist, ungefähr genauso gefährlich ist bash(auch eine Sicherheitsanfälligkeit durch Befehlsinjektion, wenn $chosen_prefixes unter der Kontrolle eines Angreifers steht) und weniger Menschen davon Kenntnis haben. Versuchen Sie es var_name='a[$(uname>&2)0]' bash -c 'v=${!var_name}'. Der beste Ansatz ist hier das assoziative Array.
Stéphane Chazelas