2012-03-15 23 views
50

È possibile eseguire il looping di tuple in bash?Loop over tuples in bash?

A titolo di esempio, sarebbe bello se i seguenti lavorato:

for (i,j) in ((c,3), (e,5)); do echo "$i and $j"; done 

Esiste una soluzione che in qualche modo mi lascia un ciclo su tuple?

+2

Venendo da sfondo python Questa è una domanda davvero molto utile! –

+3

guardando questo quattro anni dopo mi chiedo se non c'è ancora un modo migliore per farlo. Oh mio Dio. – Giszmo

risposta

58
$ for i in c,3 e,5; do IFS=","; set -- $i; echo $1 and $2; done 
c and 3 
e and 5 

A proposito di questo uso di set (da man builtins):

Qualsiasi argomento rimanenti dopo l'elaborazione opzione sono trattate come valori per i parametri posizionali e vengono assegnati, in ordine, a $ 1, $ 2, ... $ n

il IFS="," imposta il separatore di campo in modo che ogni $i ottiene segmentato in $1 e $2 correttamente.

Via this blog.

Edit: la versione più corretta, come suggerito da @SLACEDIAMOND:

$ OLDIFS=$IFS; IFS=','; for i in c,3 e,5; do set -- $i; echo $1 and $2; done; IFS=$OLDIFS 
c and 3 
e and 5 
+7

Nice - voglio solo sottolineare che 'IFS' dovrebbe essere salvato e resettato al suo valore originale se questo viene eseguito sulla riga di comando. Inoltre, il nuovo 'IFS' può essere impostato una volta, prima dell'esecuzione del ciclo, piuttosto che ogni iterazione. – Devourant

+1

@SLACEDIAMOND: grazie, ho incorporato i tuoi suggerimenti. –

+0

Nel caso in cui uno dei $ i inizia con un trattino, è più sicuro di 'set - $ i' –

0

Un po 'più complesso, ma può essere utile:

a='((c,3), (e,5))' 
IFS='()'; for t in $a; do [ -n "$t" ] && { IFS=','; set -- $t; [ -n "$1" ] && echo i=$1 j=$2; }; done 
4
$ echo 'c,3;e,5;' | while IFS=',' read -d';' i j; do echo "$i and $j"; done 
c and 3 
e and 5 
6
c=('a' 'c') 
n=(3 4) 

for i in $(seq 0 $((${#c[*]}-1))) 
do 
    echo ${c[i]} ${n[i]} 
done 

Potrebbe essere a volte più maneggevole.

Per spiegare la parte ugly, come indicato nelle osservazioni:

seq 0 2 produce la sequenza numerica 0 1 2 $ (cmd) è la sostituzione di comando, quindi per questo esempio l'output di seq 0 2 , che è la sequenza numerica. Ma qual è il limite superiore, lo $((${#c[*]}-1))?

$ ((somthing)) è l'espansione aritmetica, quindi $ ((3 + 4)) è 7 ecc. La nostra espressione è ${#c[*]}-1, quindi qualcosa - 1. Molto semplice, se sappiamo cosa è ${#c[*]}.

c è un array, c [*] è solo l'intero array, $ {# c [*]} è la dimensione dell'array che è 2 nel nostro caso. Ora riavvolgiamo tutto: for i in $(seq 0 $((${#c[*]}-1))) è for i in $(seq 0 $((2-1))) è for i in $(seq 0 1) è for i in 0 1. Poiché l'ultimo elemento dell'array ha un indice che corrisponde alla lunghezza dell'array - 1.

+1

si dovrebbe fare 'per i in $ (seq 0 $ (($ # c [*]} - 1))); do [...] ' – reox

+0

@reox: Fatto. Grazie. –

+1

Wow, questo vince il premio "Ugliest Bunch of Arbitrary Characters I've Seen Today". Qualcuno si preoccupa di spiegare cosa fa esattamente questo abominio? Mi sono perso al cancelletto ... – koniiiik

10

Credo che questa soluzione sia un po 'più pulita rispetto alle altre che sono state inviate, h/t alla guida di stile bash this per illustrare in che modo può essere letto per dividere le stringhe in un delimitatore e assegnarle a singole variabili.

for i in c,3 e,5; do 
    IFS=',' read item1 item2 <<< "${i}" 
    echo "${item1}" and "${item2}" 
done 
1

Utilizzando GNU parallelo:

parallel echo {1} and {2} ::: c e :::+ 3 5 

Oppure:

parallel -N2 echo {1} and {2} ::: c 3 e 5 

Oppure:

parallel --colsep , echo {1} and {2} ::: c,3 e,5 
+1

Nessun amore per questo? ben fatto * io * superare l'inerzia e installare 'gnu parallel' – javadba

+1

' brew install parallel' – javadba

0

Usa array associativo (noto anche come dizionario/HashMap):

declare -A pairs=(
    [c]=3 
    [e]=5 
) 
for key in "${!pairs[@]}"; do 
    value="${pairs[$key]}" 
    echo "key is $key and value is $value" 
done 

Lavori per la bash4.0 +.


Se ti senti si può in una sola esigenza giorno triplica invece di coppie, è possibile utilizzare l'approccio più generale:

animals=(dog cat mouse) 
declare -A sound=(
    [dog]=barks 
    [cat]=purrs 
    [mouse]=cheeps 
) 
declare -A size=(
    [dog]=big 
    [cat]=medium 
    [mouse]=small 
) 
for animal in "${animals[@]}"; do 
    echo "$animal ${sound[$animal]} and it is ${size[$animal]}" 
done