11

我有两个长度相等的列表,各个项目中没有空格:

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

我想并行迭代这两个列表,将 a 与 1 配对,将 b 与 2 配对,等等:

a 1
b 2
c 3

我正在尝试支持现代便携式 Bourne shell,因此 Bash/ksh 数组不可用。在紧要关头对 awk 进行炮击是可以接受的,但如果可能的话,我宁愿将其保留在纯 sh 中。

感谢您提供的任何指示!

4

9 回答 9

15

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
于 2009-02-13T18:19:04.490 回答
5

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.$$
于 2009-02-13T18:24:41.653 回答
4

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
于 2009-02-13T18:00:22.633 回答
3

This should be portable and also works with more than two lists:

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

Using positional paramers works, too. Please note, the list elements may not start with "-". Otherwise "set" will fail.

#!/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
于 2015-01-25T13:11:34.730 回答
1

没关系,看到“BOURNE”并想到“BOURNE AGAIN”。把它留在这里是因为它可能对某人有用,但显然不是问题的答案,对不起!

--

这有一些缺点(它不能优雅地处理不同大小的列表),但它适用于您给出的示例:

#!/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
于 2009-02-13T17:55:01.243 回答
1

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
于 2009-02-13T18:18:10.390 回答
1

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
于 2009-02-13T18:28:37.947 回答
1

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.

于 2009-02-14T00:57:13.833 回答
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
于 2012-10-26T01:17:45.800 回答