shell - script - Iterando en dos listas en paralelo en/bin/sh
shell script example (9)
Esta debería ser una solución bastante limpia, pero a menos que use la bash
de proceso de bash
, requiere el uso de archivos temporales. No sé si eso es mejor o peor que invocar 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.$$
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, emparejando a con 1, b con 2, etc.
a 1
b 2
c 3
Estoy intentando admitir el moderno shell Bourne portátil, por lo que las matrices Bash / ksh 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 sugerencia que pueda proporcionar!
Esto es un poco raro, 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
NEVERMIND, VIENE "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 algunas deficiencias (no maneja con gracia listas que son de diferentes tamaños), pero funciona para el ejemplo que usted dio:
#!/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 usando herramientas comunes de Unix como awk y cut, pero lo anterior es una implementación de pure-bash como se solicita
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ó:
#!/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
Probablemente no sea portable (¡mire todos esos bash-ismos!), Pero es fácil de leer y alguien más puede encontrarle ú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
Solución que no usa 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
Estuve trabajando en una respuesta basada en sed cuando aparecieron las primeras soluciones aquí. Pero luego de una investigación más profunda, resultó que los elementos en la lista estaban separados por líneas nuevas, no espacios, lo que me permitió ir 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 solo en caso de que alguien se encuentre con esta variante del problema, pero otorgo la respuesta aceptada basada en el problema tal como se publicó.
$ 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
Esto debería ser portátil y también funciona 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 parámeros posicionales también funciona. 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
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