سؤال باش: تتكرر عبر الخطوط في متغير


كيف يمكن للمرء أن يتكرر بشكل صحيح عبر الخطوط في bash سواء في المتغير ، أو من إخراج الأمر؟ يعمل ببساطة إعداد متغير IFS على سطر جديد على إخراج الأمر ولكن ليس عند معالجة متغير يحتوي على أسطر جديدة.

فمثلا

#!/bin/bash

list="One\ntwo\nthree\nfour"

#Print the list with echo
echo -e "echo: \n$list"

#Set the field separator to new line
IFS=$'\n'

#Try to iterate over each line
echo "For loop:"
for item in $list
do
        echo "Item: $item"
done

#Output the variable to a file
echo -e $list > list.txt

#Try to iterate over each line from the cat command
echo "For loop over command output:"
for item in `cat list.txt`
do
        echo "Item: $item"
done

هذا يعطي الناتج:

echo: 
One
two
three
four
For loop:
Item: One\ntwo\nthree\nfour
For loop over command output:
Item: One
Item: two
Item: three
Item: four

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


176
2018-05-16 12:14


الأصل




الأجوبة:


باستخدام bash ، إذا كنت تريد تضمين خطوط جديدة في سلسلة ، فأرفق السلسلة مع $'':

$ list="One\ntwo\nthree\nfour"
$ echo "$list"
One\ntwo\nthree\nfour
$ list=$'One\ntwo\nthree\nfour'
$ echo "$list"
One
two
three
four

وإذا كان لديك مثل هذه السلسلة بالفعل في متغير ، فيمكنك قراءتها بخط تلو الآخر مع:

while read -r line; do
    echo "... $line ..."
done <<< "$list"

226
2018-05-16 13:47



مجرد ملاحظة أن done <<< "$list" حاسم - Jason Axelson
السبب done <<< "$list" هو أمر بالغ الأهمية لأن ذلك سوف يمر "قائمة $" كمدخل ل read - wisbucky
أحتاج أن أشدد على هذا: ازدواجية القيمة $list أمر بالغ الأهمية. - André Chalella
كيف أفعل ذلك في الرماد؟ لأنني أحصل على syntax error: unexpected redirection. - atripes
echo "$list" | while ... يمكن أن تظهر أكثر وضوحا من ... done <<< "$line" - kyb


يمكنك استخدام while + read:

some_command | while read line ; do
   echo === $line ===
done

راجع للشغل. ال -e خيار ل echo غير قياسي. استعمال printf بدلا من ذلك ، إذا كنت تريد قابلية.


51
2018-05-16 12:21



لاحظ أنه في حالة استخدام بناء الجملة هذا ، فلن يتم الاحتفاظ بالمتغيرات التي تم تعيينها داخل الحلقة بعد الحلقة. الغريب بما فيه الكفاية ، و <<< نسخة اقترحها غلين جاكمان هل العمل مع الواجب المتغير. - Sparhawk
Sparhawk نعم ، هذا لأن الأنابيب تبدأ في تنفيذ قاع while جزء. ال <<< الإصدار لا (في إصدارات bash الجديدة ، على الأقل). - maxelost


#!/bin/sh

items="
one two three four
hello world
this should work just fine
"

IFS='
'
count=0
for item in $items
do
  count=$((count+1))
  echo $count $item
done

21
2018-03-17 21:45



هذا يخرج جميع العناصر ، وليس خطوط. - Tomasz Posłuszny
@ TomaszPosłuszny لا ، هذه المخرجات 3 (العد هو 3) خطوط على الجهاز الخاص بي. لاحظ إعداد IFS. يمكنك أيضا استخدام IFS=$'\n' لنفس التأثير. - jmiserez


إليك طريقة مضحكة لعمل حلقة for:

for item in ${list//\\n/
}
do
   echo "Item: $item"
done

سيكون أكثر معقولية / قابلة للقراءة:

cr='
'
for item in ${list//\\n/$cr}
do
   echo "Item: $item"
done

ولكن هذا معقد للغاية ، فأنت تحتاج فقط إلى مساحة هناك:

for item in ${list//\\n/ }
do
   echo "Item: $item"
done

أنت $line لا يحتوي المتغير على خطوط جديدة. يحتوي على أمثلة \ تليها n. يمكنك رؤية ذلك بوضوح مع:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo $list | hexdump -C

$ ./t.sh
00000000  4f 6e 65 5c 6e 74 77 6f  5c 6e 74 68 72 65 65 5c  |One\ntwo\nthree\|
00000010  6e 66 6f 75 72 0a                                 |nfour.|
00000016

يحل الاستبدال محل تلك التي تحتوي على مسافات ، وهو ما يكفي لعملها في الحلقات:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\\n/ } | hexdump -C

$ ./t.sh 
00000000  4f 6e 65 20 74 77 6f 20  74 68 72 65 65 20 66 6f  |One two three fo|
00000010  75 72 0a                                          |ur.|
00000013

عرض:

$ cat t.sh
#! /bin/bash
list="One\ntwo\nthree\nfour"
echo ${list//\\n/ } | hexdump -C
for item in ${list//\\n/ } ; do
    echo $item
done

$ ./t.sh 
00000000  4f 6e 65 20 74 77 6f 20  74 68 72 65 65 20 66 6f  |One two three fo|
00000010  75 72 0a                                          |ur.|
00000013
One
two
three
four

7
2018-05-16 12:45



مثير للاهتمام ، هل يمكنك شرح ما يجري هنا؟ يبدو أنك تستبدل \ n بخط جديد ... ما الفرق بين السلسلة الأصلية والجديدة؟ - Alex Spurling
@ أليكس: تحديث إجابتي - مع إصدار أبسط أيضا :-) - Mat
لقد جربت ذلك ولا يبدو أنه يعمل إذا كان لديك مساحات في مدخلاتك. في المثال أعلاه ، لدينا "\ ntwo \ n ثلاثة" وهذا يعمل ولكن يفشل إذا كان لدينا "إدخال \ nentry \ nentry three" لأنه يضيف أيضًا سطرًا جديدًا للمساحة أيضًا. - John Rocha
مجرد ملاحظة أنه بدلا من تحديد cr يمكنك استخدام $'\n'. - devios1