Iterar sobre dos listas en paralelo en / bin / sh
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!
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ó.