tags:

views:

442

answers:

7

I have two lists of equal length, with no spaces in the individual items:

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

I want to iterate over these two lists in parallel, pairing a with 1, b with 2, etc.:

a 1
b 2
c 3

I'm attempting to support modern portable Bourne shell, so Bash/ksh arrays aren't available. Shelling out to awk would be acceptable in a pinch, but I'd rather keep this in pure sh if possible.

Thank you for any pointers you can provide!

+1  A: 

NEVERMIND, SAW "BOURNE" and thought "BOURNE AGAIN". Leaving this here because it might be useful for someone, but clearly not the answer to the question asked, sorry!

--

This has some shortcomings (it doesn't gracefully handle lists that are different sizes), but it works for the example you gave:

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

There are more graceful ways using common unix tools like awk and cut, but the above is a pure-bash implementation as requested

Commenting on the accepted answer, it didn't work for me in either linux or Solaris, the problem was the \S character class shortcut in the regexp for sed. I replaced it with [^ ] and it worked:

#!/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
jj33
The accepted answer is the second in this post, based on superior portability.
emk
+2  A: 

This is a bit hacky but does the job:

#!/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
Nick Fortescue
FWIW this doesn't work under /bin/sh on a solaris server. it gets stuck in an endless loop repeating "1 a"
jj33
Oh well, the questioner has accepted so I guess it works for him. I don't have a Solaris server. If you add a comment with the correction (it will be something in the sed rexgep) I'll add it, but until then I'll leave the answer as stands.
Nick Fortescue
Commenting on the accepted answer, it didn't work for me in either linux or Solaris, the problem was the \S character class shortcut in the regexp for sed. I replaced it with [^ ] and it worked
jj33
cheers jj33, edit to more portable version is in
Nick Fortescue
If anyone has a version that works on Solaris, I'll be happy to change the accepted answer.
emk
Oh, never mind. It has been fixed.
emk
+1  A: 

As a one liner:

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

sfossen
+1  A: 

Probably not portable (look at all those bash-isms!), but it is easy to read and someone else might find it useful...

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
Ryan Graham
+1  A: 

This should be a fairly clean solution, but unless you use bash's process substition, it requires the use of temporary files. I don't know if that's better or worse than invoking cut and sed over every iteration.

#!/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.$$
Josh Kelley
+1  A: 

Solution not using arrays:

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
fgm
A: 

I had been working on a sed-based answer when the first solutions started showing up here. But upon further investigation, it turned out that the items in the list were separated by newlines, not spaces, which allowed me to go with a solution based on head and tail:

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

I'm posting this just in case somebody encounters this variant of the problem, but I'm awarding the accepted answer based on the problem as posted.

emk