在/ bin / sh中并行迭代两个列表

时间:2009-02-13 17:17:08

标签: shell sh

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

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

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

a 1
b 2
c 3

我正在尝试支持现代便携式Bourne shell,因此Bash / ksh数组不可用。如果可能的话,对awk的攻击是可以接受的,但是如果可能的话,我宁愿把它保持在纯粹的状态。

感谢您提供的任何指示!

9 个答案:

答案 0 :(得分:13)

可能不便携(看看所有那些bash-isms!),但它很容易阅读,而其他人可能觉得它很有用......

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

答案 1 :(得分:4)

这有点hacky但是做了这个工作:

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

答案 2 :(得分:4)

这应该是一个相当干净的解决方案,但除非你使用bash的进程替代,否则它需要使用临时文件。我不知道这是否比在每次迭代中调用cutsed更好或更差。

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

答案 3 :(得分:2)

这应该是可移植的,并且可以使用两个以上的列表:

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

使用位置参数也可以。请注意,列表元素不能以“ - ”开头。否则“设置”将失败。

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

答案 4 :(得分:1)

NEVERMIND,SAW“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

使用常见的unix工具(如awk和cut)有更优雅的方法,但上面是按要求的纯bash实现

评论已接受的答案,它在linux或Solaris中对我没有用,问题是sed的regexp中的\ S字符类快捷方式。我用[^]替换它并且它起作用了:

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

答案 5 :(得分:1)

作为一个班轮:

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

答案 6 :(得分:1)

解决方案不使用数组:

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

答案 7 :(得分: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

答案 8 :(得分:0)

当第一个解决方案开始出现在这里时,我一直在研究基于sed的答案。但经过进一步调查后发现,列表中的项目是用换行符分隔的,而不是空格,这让我可以选择基于头尾的解决方案:

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

我发布这个以防万一有人遇到问题的这个变种,但我会根据发布的问题批准接受的答案。