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

发布于 2024-07-13 10:58:48 字数 293 浏览 6 评论 0原文

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

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

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

a 1
b 2
c 3

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

感谢您提供的任何指示!

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!

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(9

ゝ偶尔ゞ 2024-07-20 10:58:48

可能不可移植(看看所有那些 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

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
聽兲甴掵 2024-07-20 10:58:48

这应该是一个相当干净的解决方案,但除非您使用 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.$

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.$
一个人的夜不怕黑 2024-07-20 10:58:48

这有点 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

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
稍尽春風 2024-07-20 10:58:48

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

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

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
对你再特殊 2024-07-20 10:58:48
$ 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
$ 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
伪装你 2024-07-20 10:58:48

没关系,看到“谍影重重”并想到“谍影重重”。 将其留在这里是因为它可能对某人有用,但显然不是所问问题的答案,抱歉!

--

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

#!/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 的正则表达式中的 \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

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
北音执念 2024-07-20 10:58:48

不使用数组的解决方案:

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

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
浮生未歇 2024-07-20 10:58:48

当第一个解决方案开始出现在这里时,我一直在研究基于 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

我发布此内容是为了防止有人遇到这种变体问题,但我根据发布的问题授予接受的答案。

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.

蓝天白云 2024-07-20 10:58:48

作为单行:

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

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
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文