Dereference nome variabile concatenato


12

Posso farlo, ma richiede di creare una stringa della variabile e quindi di dereferenziarla. C'è un modo per accorciarlo a una dichiarazione più semplice?

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

Ho provato molte cose come ${!"${fruit}_COLOUR"}oe \$${fruit}_COLOURmolte altre varianti, ma l'unico modo che ha funzionato è usare una stringa.


La !sintassi è una bandiera sulla sostituzione delle variabili che sostanzialmente dice "sostituisci due volte". Non cambia il requisito secondo cui ciò che è dentro ${…}deve essere un nome variabile. Quindi no, non puoi evitare di usare una variabile che contiene il nome, a meno che tu non usi un metodo diverso ( eval).
Gilles 'SO- smetti di essere malvagio' il

Puoi anche usare array associativi per farlo in modo molto più pulito. Tuttavia non è una risposta diretta alla tua domanda, quindi basta aggiungerla come commento.
Patrick,

Risposte:


11

Prima di tutto, non è necessario utilizzare $(echo $FRUITS)nella fordichiarazione. Usare solo $FRUITSè abbastanza. Quindi puoi eliminare una delle linee all'interno del loop, usando eval.

La evaldice semplicemente bash di fare una seconda valutazione della seguente dichiarazione (cioè uno in più rispetto alla sua valutazione normale). Il \$sopravvive alla prima valutazione come $, e la valutazione successiva considera questo $come l'inizio di un nome di variabile, che si risolve in "Giallo", ecc.

In questo modo non è necessario disporre di un passaggio separato che crea una stringa intermedia (che è quello che ritengo fosse lo scopo principale della tua domanda).

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

Per un metodo alternativo, come menzionato da Patrick in un commento (sopra), puoi invece usare un array associativo , in cui l'indice di un elemento non deve necessariamente essere un numero intero. È possibile utilizzare una stringa, ad esempio il nome di un tipo di frutto. Ecco un esempio, usando l'array associativo di 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

Puoi usare bash-builtin evalper farlo:

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

Nota il segno di dollaro rovesciato. Fondamentalmente, la linea "eval" fa bash sostituire $fruite ${fruit_color}, quindi, usare evalper fare un secondo giro di sottostizione prima di chiamare echo.


1

Non hai bisogno di una dichiarazione di valutazione. evalè un suggerimento nella maggior parte delle lingue che stai facendo qualcosa di sbagliato.

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}"

Direi che è più sicuro da usare evalperché almeno le persone sanno che è pericoloso, ${!var}è altrettanto pericoloso bash(anche una vulnerabilità dell'iniezione di comando se $chosen_prefixè sotto il controllo di un attaccante) e meno persone ne sono consapevoli. Prova var_name='a[$(uname>&2)0]' bash -c 'v=${!var_name}'. Il miglior approccio qui è l'array associativo.
Stéphane Chazelas,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.