2009-02-13 10 views
10

I tienen dos listas de igual longitud, sin espacios en los elementos individuales:iterar sobre dos listas en paralelo en/bin/sh

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

Quiero iterar sobre estas dos listas en paralelo, el emparejamiento de un con 1, B 2, etc .:

a 1 
b 2 
c 3 

estoy intentando apoyar moderna shell Bourne portátil, por lo Bash/ksh matrices no están disponibles. Desvanecerse a awk sería aceptable en un apuro, pero preferiría mantener esto en sh puro si es posible.

¡Gracias por cualquier información que pueda proporcionar!

Respuesta

4

Esto es un poco hacky pero cumple su cometido:

#!/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 esto no funciona en/bin/sh en un servidor Solaris. se queda atascado en un bucle sin fin repitiendo "1 a" – jj33

+0

Bueno, el interlocutor ha aceptado, así que supongo que funciona para él. No tengo un servidor Solaris. Si agrega un comentario con la corrección (será algo en el sed rexgep) lo agregaré, pero hasta entonces dejaré la respuesta como está. –

+0

Comentando la respuesta aceptada, no funcionó para mí ni en Linux ni en Solaris, el problema era el atajo de clase de caracteres \ S en la expresión regular para sed. Lo reemplacé con [^] y funcionó – jj33

1

NEVERMIND, VIO "BOURNE" y pensó "BOURNE OTRA VEZ". Dejar esto aquí porque podría ser útil para alguien, pero claramente no es la respuesta a la pregunta, ¡lo siento!

-

Esto tiene algunos defectos (no maneja con gracia listas que son diferentes tamaños), pero funciona por el ejemplo que diste:

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

Hay formas más elegantes que usan herramientas comunes de Unix como awk y cut, pero lo anterior es una implementación pura-bash como se solicitó

Al comentar sobre la respuesta aceptada, no funcionó para mí ni en linux ni en Solaris, el problema era la clase de caracteres \ S atajo en la expresión regular para sed Me lo reemplazó con [^] y funcionó:

#!/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 respuesta aceptada es la segunda en esta publicación, basada en una portabilidad superior. – emk

1

Como un trazador de líneas:

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

Probablemente no portátil (¡mira todos esos bash-ismos!), pero es fácil de leer y alguien más puede encontrarlo útil ...

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

Esta debería ser una solución bastante limpia, pero a menos que use la substición de proceso bash, requiere el uso de archivos temporales. No sé si eso es mejor o peor que invocar cut y sed en cada iteración.

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

solución no se usan matrices:

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

que había estado trabajando en una respuesta basada en la sed cuando las primeras soluciones comenzó a aparecer aquí. Sin embargo, tras una investigación, resultó que los elementos de la lista fueron separadas por saltos de línea, no espacios, lo que permitió que fuera con una solución basada en la cabeza y la cola:

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 

estoy publicando esto acaba de Si alguien encuentra esta variante del problema, otorgo la respuesta aceptada en función del problema tal como se publicó.

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

Esto debe ser portátil y también trabaja con más de dos listas:

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

Usando paramers posicionales funciona, también. Tenga en cuenta que los elementos de la lista no pueden comenzar con "-". De lo contrario, "set" fallará.

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

Para proporcionar una buena respuesta, es mejor ofrecer algún tipo de explicación de la solución en lugar de simplemente publicar el código. – Scriptable

Cuestiones relacionadas