تكرار أكثر من قائمتين بالتوازي في / بن / ش

StackOverflow https://stackoverflow.com/questions/546817

  •  23-08-2019
  •  | 
  •  

سؤال

لدي قائمتان من طول متساو، بدون مسافات في العناصر الفردية:

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

أنا نشر هذا فقط في حالة تواجه شخص ما هذا البديل من المشكلة، لكنني منح الإجابة المقبولة بناء على المشكلة باسم نشرها.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top