【发布时间】:2010-10-07 12:06:15
【问题描述】:
我有两个长度相等的列表,各个项目中没有空格:
list1="a b c"
list2="1 2 3"
我想并行迭代这两个列表,将 a 与 1 配对,b 与 2 配对,等等:
a 1
b 2
c 3
我正在尝试支持现代便携式 Bourne shell,因此 Bash/ksh 数组不可用。在紧要关头使用 awk 是可以接受的,但如果可能的话,我宁愿将其保留在纯 sh 中。
感谢您提供的任何指点!
【问题讨论】:
我有两个长度相等的列表,各个项目中没有空格:
list1="a b c"
list2="1 2 3"
我想并行迭代这两个列表,将 a 与 1 配对,b 与 2 配对,等等:
a 1
b 2
c 3
我正在尝试支持现代便携式 Bourne shell,因此 Bash/ksh 数组不可用。在紧要关头使用 awk 是可以接受的,但如果可能的话,我宁愿将其保留在纯 sh 中。
感谢您提供的任何指点!
【问题讨论】:
可能不便携(看看所有那些 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
【讨论】:
这有点老套,但确实可以:
#!/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
【讨论】:
这应该是一个相当干净的解决方案,但是除非您使用bash 的进程替换,否则它需要使用临时文件。我不知道这比在每次迭代中调用 cut 和 sed 好还是坏。
#!/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.$$
【讨论】:
这应该是可移植的,并且也适用于两个以上的列表:
#!/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
【讨论】:
没关系,看到“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
还有更优雅的方式使用 awk 和 cut 等常见的 unix 工具,但以上是应要求的纯 bash 实现
评论接受的答案,它在 linux 或 Solaris 中对我不起作用,问题是 sed 正则表达式中的 \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
【讨论】:
不使用数组的解决方案:
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
【讨论】:
当第一个解决方案开始出现在这里时,我一直在研究基于 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
我发布这个以防万一有人遇到这个问题的变体,但我会根据发布的问题授予接受的答案。
【讨论】:
作为一个班轮:
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
【讨论】: