2009-02-13 3 views
10

ho due liste di uguale lunghezza, senza spazi delle singole voci:iterazione di due liste in parallelo a/bin/sh

list1="a b c" 
list2="1 2 3" 

voglio iterare su queste due liste in parallelo, l'associazione di un con 1, B con 2, ecc .:

a 1 
b 2 
c 3 

sto cercando di sostenere moderna shell portatile Bourne, quindi Bash/matrici ksh non sono disponibili. Schizzare per awk sarebbe accettabile in un pizzico, ma preferirei tenerlo in puro sh se possibile.

Grazie per qualsiasi suggerimento che puoi fornire!

risposta

4

Questo è un po 'hacky ma non il lavoro:

#!/bin/sh 
list1="1 2 3" 
list2="a b c" 
while [ -n "$list1" ] 
do 
    head1=`echo "$list1" | cut -d ' ' -f 1` 
    list1=`echo "$list1" | sed 's/[^ ]* *\(.*\)$/\1/'` 
    head2=`echo "$list2" | cut -d ' ' -f 1` 
    list2=`echo "$list2" | sed 's/[^ ]* *\(.*\)$/\1/'` 
    echo $head1 $head2 
done 
+0

FWIW questo non funziona sotto/bin/sh su un server solaris. si blocca in un ciclo infinito ripetendo "1 a" – jj33

+0

Oh bene, l'interrogante ha accettato quindi immagino che funzioni per lui. Non ho un server Solaris. Se aggiungi un commento con la correzione (sarà qualcosa nel rexgep sed), lo aggiungerò, ma fino ad allora lascerò la risposta come valida. –

+0

Commentando la risposta accettata, non ha funzionato né per me né per Linux né per Solaris, il problema era il collegamento alla classe di carattere \ S nella regexp per sed. L'ho sostituito con [^] e ha funzionato – jj33

1

NEVERMIND, SAW "BOURNE" e pensato "BOURNE AGAIN". Lasciando questo qui perché potrebbe essere utile per qualcuno, ma chiaramente non è la risposta alla domanda, scusa!

-

Questo ha alcune carenze (non gestisce con garbo liste di differenti formati), ma funziona per l'esempio che ha dato:

#!/bin/bash 

list1="a b c" 
list2="1 2 3" 

c=0 
for i in $list1 
do 
    l1[$c]=$i 
    c=$(($c+1)) 
done 

c=0 
for i in $list2 
do 
    echo ${l1[$c]} $i 
    c=$(($c+1)) 
done 

Ci sono modi più graziosi che utilizzano comuni strumenti unix come awk e cut, ma quanto sopra è un'implementazione pure-bash come richiesto

Commentando la risposta accettata, non ha funzionato né per me né per Linux né per Solaris, il problema era la classe di caratteri \ S scorciatoia nel regexp per sed. Ho sostituito con [^] e ha funzionato:

#!/bin/sh 
list1="1 2 3" 
list2="a b c" 
while [ -n "$list1" ] 
do 
    head1=`echo "$list1" | cut -d ' ' -f 1` 
    list1=`echo "$list1" | sed 's/[^ ]* *\(.*\)$/\1/'` 
    head2=`echo "$list2" | cut -d ' ' -f 1` 
    list2=`echo "$list2" | sed 's/[^ ]* *\(.*\)$/\1/'` 
    echo $head1 $head2 
done 
+0

La risposta accettata è la seconda in questo post, basata sulla portabilità superiore. – emk

1

Come uno di linea:

list2="1 2 3"; 
list1="a b c"; 
for i in $list1; do 
    x=`expr index "$list2" " "`; 
    [ $x -eq 0 ] && j=$list2 || j=${list2:0:$x}; 
    list2=${list2:$x}; 
    echo "$i $j"; 
done 
12

Probabilmente non portatili (guarda tutti quei bash-ismi!), ma è facile da leggere e qualcun altro potrebbe trovarlo utile ...

list1="a b c" 
list2="1 2 3" 
array1=($list1) 
array2=($list2) 

count=${#array1[@]} 
for i in `seq 1 $count` 
do 
    echo ${array1[$i-1]} ${array2[$i-1]} 
done 
4

Questa dovrebbe essere una soluzione abbastanza pulita, ma a meno che non si usi la sottoschera di processo di bash, è necessario l'uso di file temporanei. Non so se sia meglio o peggio che invocare cut e sed per ogni iterazione.

#!/bin/sh 

list1="1 2 3" 
list2="a b c" 
echo $list1 | sed 's/ /\n/g' > /tmp/a.$$ 
echo $list2 | sed 's/ /\n/g' > /tmp/b.$$ 

paste /tmp/a.$$ /tmp/b.$$ | while read item1 item2; do 
    echo $item1 - $item2 
done 

rm /tmp/a.$$ 
rm /tmp/b.$$ 
1

Soluzione non utilizzo di matrici:

list1="aaa1 aaa2 aaa3" 
list2="bbb1 bbb2 bbb3" 

tmpfile1=$(mktemp /tmp/list.XXXXXXXXXX) || exit 1 
tmpfile2=$(mktemp /tmp/list.XXXXXXXXXX) || exit 1 

echo $list1 | tr ' ' '\n' > $tmpfile1 
echo $list2 | tr ' ' '\n' > $tmpfile2 

paste $tmpfile1 $tmpfile2 

rm --force $tmpfile1 $tmpfile2 
0

Avevo lavorato su una risposta basata su sed quando le prime soluzioni iniziato mostrando qui. Ma dopo ulteriori indagini, si è scoperto che gli elementi della lista sono stati separati da capo, non gli spazi, che mi hanno permesso di andare con una soluzione basata su testa e la coda:

original_revs="$(cd original && git rev-parse --all)" && 
working_revs="$(cd working && git rev-parse --all)" && 
while test -n "$original_revs"; do 
    original_commit="$(echo "$original_revs" | head -n 1)" && 
    working_commit="$(echo "$working_revs" | head -n 1)" && 
    original_revs="$(echo "$original_revs" | tail -n +2)" && 
    working_revs="$(echo "$working_revs" | tail -n +2)" && 
    ... 
done 

sto postando questo solo in caso qualcuno incontri questa variante del problema, ma sto assegnando la risposta accettata in base al problema come postata.

1
$ list1="1 2 3" 
$ list2="a b c" 
$ echo "$list1 $list2" | awk '{n=NF/2; for (i=1;i<=n;i++) print $i,$(n+i) }' 
1 a 
2 b 
3 c 
2

Questo dovrebbe essere portatile e funziona anche con più di due liste:

#!/bin/sh 
x="1 2 3 4 5" 
y="a b c d e" 
z="A B C D E" 

while 
    read current_x x <<EOF 
$x 
EOF 

    read current_y y <<EOF 
$y 
EOF 

    read current_z z <<EOF 
$z 
EOF 

    [ -n "$current_x" ] 
do 
    echo "x=$current_x y=$current_y z=$current_z" 
done 

Utilizzando paramers posizionali funziona, anche. Si prega di notare che gli elementi della lista non possono iniziare con "-". Altrimenti "set" fallirà.

#!/bin/sh 

x="1 2 3 4 5" 
y="a b c d e" 
z="A B C D E" 

while 
    [ -n "$x" ] 
do 
    set $x 
    current_x=$1 
    shift 
    x="$*" 

    set $y 
    current_y=$1 
    shift 
    y="$*" 

    set $z 
    current_z=$1 
    shift 
    z="$*" 

    echo "x=$current_x y=$current_y z=$current_z" 
done 
+0

Per fornire una buona risposta, è meglio offrire una sorta di spiegazione della soluzione piuttosto che pubblicare semplicemente il codice. – Scriptable