Pregunta

Tengo dos listas de igual longitud, sin espacios en los elementos individuales:

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

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

a 1
b 2
c 3

Estoy intentando apoyar moderna portátil Bourne shell, por lo Bash / ksh matrices no están disponibles. Los bombardeos a cabo a awk sería aceptable en un apuro, pero prefiero tener esto en sh pura si es posible.

Gracias por cualquier punteros que puede proporcionar!

¿Fue útil?

Solución

Esto es un poco hacky, pero hace el trabajo:

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

Otros consejos

Probablemente no portátil (mira todas esas Bash-ismos!), Pero es fácil de leer y alguien más podría encontrar ú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

Esto debe ser una solución bastante limpio, pero a menos que utilice SUSTITUCIÓN proceso de bash, se requiere el uso de archivos temporales. No sé si eso es mejor o peor que la invocación de cut y sed sobre 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.$$

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

El uso de paramers posicionales obras, también. Tenga en cuenta que los elementos de la lista no pueden comenzar con "-". De otro modo "set" se producirá un error.

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

No importa, SAW "Bourne" y pensó "Bourne Again". Dejando esto aquí, ya que podría ser útil para alguien, pero es evidente que no es la respuesta a la pregunta formulada, 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

Existen formas más elegantes utilizando herramientas comunes de Unix como awk y corte, pero lo anterior es una aplicación pura-bash como solicitó

Al comentar sobre la respuesta aceptada, no funcionó para mí en Linux o Solaris, el problema era el \ S atajo clase de caracteres en la expresión regular para la 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

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

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
$ 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

Yo había estado trabajando en una respuesta basada en la sed cuando las primeras soluciones empezaron 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 publicar esto sólo en caso de que alguien se encuentra con esta variante del problema, pero estoy concesión de la respuesta aceptada sobre la base de los problemas y se registró.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top