2009-02-13 38 views
10

Tôi có hai danh sách có độ dài bằng nhau, không có khoảng trống trong các mục riêng:iterating trên hai danh sách song song trong/bin/sh

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

Tôi muốn để lặp qua hai danh sách này song song, ghép nối một với 1, b với 2, v.v.:

a 1 
b 2 
c 3 

Tôi đang cố gắng hỗ trợ vỏ Bourne di động hiện đại, vì vậy mảng Bash/ksh không khả dụng. Shelling ra để awk sẽ được chấp nhận trong một pinch, nhưng tôi muốn giữ điều này trong sh tinh khiết nếu có thể.

Cảm ơn bạn về bất kỳ con trỏ nào bạn có thể cung cấp!

Trả lời

4

Đây là một chút hacky nhưng công việc:

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

FWIW tính năng này không hoạt động trong/bin/sh trên máy chủ solaris. nó bị mắc kẹt trong một vòng lặp vô tận lặp đi lặp lại "1 a" – jj33

+0

Ồ, người hỏi đã chấp nhận vì vậy tôi đoán nó hoạt động cho anh ta. Tôi không có máy chủ Solaris. Nếu bạn thêm một bình luận với sửa chữa (nó sẽ là một cái gì đó trong sed rexgep) Tôi sẽ thêm nó, nhưng cho đến khi đó tôi sẽ để lại câu trả lời như là viết tắt. –

+0

Nhận xét về câu trả lời được chấp nhận, nó không hoạt động đối với tôi trong linux hoặc Solaris, vấn đề là phím tắt lớp ký tự \ S trong regexp cho sed. Tôi thay thế nó bằng [^] và nó hoạt động – jj33

1

NEVERMIND, SAW "BOURNE" và nghĩ "BOURNE AGAIN". Rời khỏi đây vì nó có thể hữu ích cho ai đó, nhưng rõ ràng không phải là câu trả lời cho câu hỏi, xin lỗi!

-

này có một số thiếu sót (nó không duyên dáng xử lý danh sách có kích cỡ khác nhau), nhưng nó hoạt động cho ví dụ bạn đưa ra:

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

Có nhiều cách duyên dáng hơn khi sử dụng các công cụ unix phổ biến như awk và cut, nhưng ở trên là việc triển khai thuần túy theo yêu cầu

Nhận xét về câu trả lời được chấp nhận, nó không hoạt động với Linux hoặc Solaris, vấn đề là lớp nhân vật \ S phím tắt trong regexp cho sed. Tôi thay thế nó với [^] và nó làm việc:

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

Câu trả lời được chấp nhận là câu trả lời thứ hai trong bài đăng này, dựa trên tính di động cao cấp. – emk

1

Như một 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 
12

Có lẽ không cầm tay (nhìn vào tất cả những bash-isms!), nhưng nó rất dễ đọc và ai đó có thể tìm thấy nó hữu ích ...

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 
4

Đây phải là một giải pháp khá sạch sẽ, nhưng trừ khi bạn sử dụng trạm biến áp quá trình bash, nó yêu cầu sử dụng các tệp tạm thời. Tôi không biết nếu đó là tốt hơn hay tệ hơn là gọi cutsed trên mọi lần lặp lại.

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

Giải pháp không sử dụng mảng:

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 
0

tôi đã làm việc trên một câu trả lời sed dựa trên khi các giải pháp đầu tiên bắt đầu xuất hiện ở đây. Nhưng khi điều tra thêm, hóa ra là các mục trong danh sách được phân cách bằng dòng mới, không phải khoảng trắng, cho phép tôi đi với một giải pháp dựa trên đầu và đuôi:

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 

Tôi đang đăng bài này chỉ trong trường hợp ai đó gặp phải biến thể này của vấn đề, nhưng tôi đang trao giải câu trả lời được chấp nhận dựa trên vấn đề như được đăng.

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 
2

này nên được cầm tay và cũng làm việc với hơn hai danh sách:

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

Sử dụng paramers vị trí làm việc, quá. Xin lưu ý, các phần tử danh sách có thể không bắt đầu bằng "-". Nếu không, "set" sẽ thất bại.

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

Để cung cấp câu trả lời hay nhất, tốt nhất nên cung cấp một số giải thích về giải pháp thay vì chỉ đăng mã. – Scriptable

Các vấn đề liên quan