تكرار أكثر من قائمتين بالتوازي في / بن / ش
سؤال
لدي قائمتان من طول متساو، بدون مسافات في العناصر الفردية:
list1="a b c"
list2="1 2 3"
أريد التأكيد على هاتين القائمتين بالتوازي، إقران A مع 1، B مع 2، إلخ.:
a 1
b 2
c 3
أحاول دعم قذيفة بورن الحديثة المحمولة، لذلك لا تتوفر المصفوفات باش / KSH. القصف إلى AWK سيكون مقبولا في قرصة، لكنني أفضل الحفاظ على هذا في SH إذا كان ذلك ممكنا.
شكرا لك على أي مؤشرات يمكنك تقديمها!
المحلول
هذا هو المخربقة قليلا ولكن الوظيفة:
#!/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
نصائح أخرى
ربما غير محمول (إلقاء نظرة على جميع تلك الباش 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
يجب أن يكون هذا حلا نظيفا إلى حد ما، ولكن ما لم تستخدم 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
nevermind، شهدت "بورن" وفكرت "بورن مرة أخرى". ترك هذا هنا لأنه قد يكون مفيدا لشخص ما، ولكن من الواضح أنه ليس الجواب على السؤال، آسف!
--
هذا له بعض أوجه القصور (لا يتعامل بأمان من القوائم التي هي أحجام مختلفة)، لكنها تعمل على سبيل المثال أعطيتها:
#!/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 وقطعها، ولكن ما سبق هو تطبيق باش باش كما هو مطلوب
التعليق على الإجابة المقبولة، لم تنجح بالنسبة لي في نظام Linux أو Solaris، كانت المشكلة اختصار الطبقة الشخصية في RegexP ل SED. لقد حل محلها مع [^] وعملت:
#!/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
كإطار واحد:
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
كنت أعمل في إجابة قائمة على 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
أنا نشر هذا فقط في حالة تواجه شخص ما هذا البديل من المشكلة، لكنني منح الإجابة المقبولة بناء على المشكلة باسم نشرها.