2012-06-24 10 views
15

Jak mogę sprawić, aby ten kod zadziałał?Jak iterować po tablicy przy użyciu pośredniej referencji?

#!/bin/bash 
ARRAYNAME='FRUITS' 
FRUITS=(APPLE BANANA ORANGE) 
for FRUIT in ${!ARRAYNAME[@]} 
do 
    echo ${FRUIT} 
done 

ten kod:

echo ${!ARRAYNAME[0]} 

Drukuje APPLE. Próbuję zrobić coś podobnego, ale "[@]" do iteracji po tablicy.

Dzięki z góry,

+4

proszę zobaczyć [BashFAQ/006] (http://mywiki.wooledge.org/BashFAQ/006). –

Odpowiedz

18

${!ARRAYNAME[@]} oznacza "Wskaźniki ARRAYNAME". Jak podano w bash man page od ARRAYNAME jest ustawiony, ale jako ciąg, a nie tablica, zwraca 0.

Oto rozwiązanie z użyciem eval.

#!/usr/bin/env bash 

ARRAYNAME='FRUITS' 
FRUITS=(APPLE BANANA ORANGE) 

eval array=\(\${${ARRAYNAME}[@]} \) 

for fruit in "${array[@]}"; do 
    echo ${fruit} 
done 

czego pierwotnie próbuje zrobić, to stworzyć Indirect Reference. Zostały one wprowadzone w bash w wersji 2 i miały w znacznym stopniu zastąpić potrzebę eval, gdy próbują osiągnąć zachowanie podobne do odbicia w powłoce.

Co trzeba zrobić przy użyciu odwołań pośrednich z tablicami jest zawierać [@] w swoim odgadnąć nazwy zmiennej:

#!/usr/bin/env bash 

ARRAYNAME='FRUITS' 
FRUITS=(APPLE BANANA ORANGE) 

array="${ARRAYNAME}[@]" 
for fruit in "${!array}"; do 
    echo $fruit 
done 

Wszystko, co powiedział, to jest jedna rzecz, aby korzystać pośrednie odniesienia w tym trywialny przykład, ale, jak wskazano w odnośniku dostarczonym przez Dennisa Williamsona, powinieneś wahać się, czy użyć ich w skryptach z prawdziwego świata. Są z pewnością gwarantem, że twój kod będzie bardziej zagmatwany, niż to konieczne. Zwykle można uzyskać potrzebną funkcjonalność dzięki asocjacyjnej macierzy.

+0

Tim: twoja druga część odpowiedzi nie jest przydatna, ponieważ ARRAYNAME jest parametrem wejściowym mojego skryptu. Ale twoje pierwsze rozwiązanie pozwala mi znaleźć bardziej eleganckie rozwiązanie: ARRAY = $ {! ARRAYNAME}; dla OWOCÓW w $ {ARRAY [@]} ... Dzięki – Neuquino

+0

@Neuquino Zobacz moją zmienioną odpowiedź. Wszystko, co musisz zrobić, to połączyć '[@]' na końcu zmiennej wejściowej. –

+2

@Neuquino Jeśli ARRAYNAME jest parametrem wejściowym skryptu, robisz coś strasznego. Nie ma powodów, aby mieszać dane wprowadzane przez użytkownika z nazwami zmiennych. W tym przypadku (w połączeniu z rozszerzeniem pośrednim), pozwala to na dowolny wtrysk kodu. TYLKO uzasadniony powód tych technik jest wykorzystywany w funkcjach, nigdy w zakresie globalnym i nigdy w połączeniu z wprowadzaniem danych przez użytkownika. – ormaaj

0

Chciałem tylko dodać kolejny przydatny przypadek użycia. Szukałem w internecie rozwiązania inna, ale problem związany

ARRAYNAME=(FRUITS VEG) 
FRUITS=(APPLE BANANA ORANGE) 
VEG=(CARROT CELERY CUCUMBER) 
for I in "${ARRAYNAME[@]}" 
do 
    array="${I}[@]" 
    for fruit in "${!array}"; do 
     echo $fruit 
    done 
done 
+0

'tablica =" $ {I} [@] "' jest dokładnie takie samo, jak 'tmp = $ nazwa_architektury [@]'! Nie ma poprawy! –

0

Pomimo prostej OP pytanie, odpowiedzi te nie będą skalowane do najczęstszych, rzeczywistych przypadków użycia, czyli elementy tablicy zawierającej spacje lub symboli wieloznacznych, które nie powinny jeszcze zostać rozszerzone na nazwy plików.

FRUITS=(APPLE BANANA ORANGE 'not broken' '*.h') 
ARRAYNAME=FRUITS 
eval ARRAY=\(\${$ARRAYNAME[@]}\) 

$ echo "${ARRAY[4]}" 
broken 
$ echo "${ARRAY[5]}" 
config.h 
$ 

to działa:

FRUITS=(APPLE BANANA ORANGE 'not broken' '*.h') 
ARRAYNAME=FRUITS 
eval ARRAY="(\"\${$ARRAYNAME[@]}\")" 

$ echo "${ARRAY[3]}" 
not broken 
$ echo "${ARRAY[4]}" 
*.h 
$ 

Podobnie jak powinieneś dostać w zwyczaju korzystania "[email protected]" nie [email protected], zawsze zacytować wewnątrz () do rozbudowy macierzy, chyba że chcesz rozszerzenie nazwy pliku lub wiedzą, że nie ma możliwości elementów tablicy zawierających białe spacje.

Wykonaj: X=("${Y[@]}")

Nie tym: X=(${Y[@]})

8

oto sposób to zrobić bez eval.

Zobacz Bash Trick # 2 opisane tutaj: http://mywiki.wooledge.org/BashFAQ/006

wydaje się działać w bash 3 i do góry.

#!/bin/bash 

ARRAYNAME='FRUITS' 
tmp=$ARRAYNAME[@] 
FRUITS=(APPLE BANANA ORANGE "STAR FRUIT") 
for FRUIT in "${!tmp}" 
do 
    echo "${FRUIT}" 
done 

Oto bardziej realistyczny przykład pokazujący jak przekazać tablicę poprzez odniesienie do funkcji:

pretty_print_array() { 
    local arrayname=$1 
    local tmp=$arrayname[@] 
    local array=("${!tmp}") 
    local FS=', ' # Field seperator 
    local var 
    # Print each element enclosed in quotes and separated by $FS 
    printf -v var "\"%s\"$FS" "${array[@]}" 
    # Chop trailing $FS 
    var=${var%$FS} 
    echo "$arrayname=($var)" 
} 
FRUITS=(APPLE BANANA ORANGE "STAR FRUIT") 
pretty_print_array FRUITS 
# prints FRUITS=("APPLE", "BANANA", "ORANGE", "STAR FRUIT") 
+0

jakikolwiek sposób, aby uzyskać rozmiar takiej tablicy pośredniej bez eval też? –

0

eval wykonuje kod zawierający elementy tablicy, nawet jeśli zawierają, na przykład, podstawienia polecenia. Zmienia także elementy tablicy, interpretując w nich metaznaki basha.

Singiel narzędzie, które pozwala na uniknięcie tych problemów jest odniesienia:

#!/bin/bash 
declare -n ARRAYNAME='FRUITS' 
FRUITS=(APPLE BANANA ORANGE "BITTER LEMON") 
for FRUIT in "${ARRAYNAME[@]}" 
do 
    echo "${FRUIT}" 
done 
Powiązane problemy