सवाल बैश: एक चर में लाइनों पर Iterating


एक वैरिएबल में या कमांड के आउटपुट से बैश में लाइनों पर सही ढंग से फिर से कैसे चलते हैं? बस एक कमांड के आउटपुट के लिए आईएफएस वैरिएबल को एक नई लाइन में काम करता है लेकिन नई लाइन वाली चर को प्रोसेस करते समय नहीं।

उदाहरण के लिए

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


मूल




जवाब:


बैश के साथ, यदि आप स्ट्रिंग में न्यूलाइन एम्बेड करना चाहते हैं, तो स्ट्रिंग को संलग्न करें $'':

$ 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

Btw। -e विकल्प echo गैर मानक है। उपयोग printf इसके बजाय, अगर आप पोर्टेबिलिटी चाहते हैं।


51
2018-05-16 12:21



ध्यान दें कि यदि आप इस वाक्यविन्यास का उपयोग करते हैं, तो लूप के अंदर आवंटित चर लूप के बाद चिपके नहीं रहेंगे। विचित्र रूप से पर्याप्त, <<< ग्लेन जैकमैन द्वारा सुझाए गए संस्करण कर देता है परिवर्तनीय असाइनमेंट के साथ काम करते हैं। - Sparhawk
@Sparhawk हां, ऐसा इसलिए है क्योंकि पाइप निष्पादित करने के लिए एक सबहेल शुरू होता है while अंश। <<< संस्करण नहीं है (कम से कम नए बैश संस्करणों में)। - 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=$'\n' एक ही प्रभाव के लिए। - jmiserez


लूप के लिए अपना काम करने का एक मजेदार तरीका यहां दिया गया है:

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
@Alex: मेरा उत्तर अपडेट किया गया - एक सरल संस्करण के साथ भी :-) - Mat
मैंने इसे आजमाया और यदि आपके इनपुट में रिक्त स्थान हैं तो यह काम नहीं करता है। ऊपर दिए गए उदाहरण में हमारे पास "एक \ ntwo \ nthree" है और यह काम करता है लेकिन यह विफल रहता है अगर हमारे पास "प्रविष्टि एक \ nentry दो \ nentry तीन" है क्योंकि यह भी अंतरिक्ष के लिए एक नई पंक्ति जोड़ता है। - John Rocha
सिर्फ एक नोट है कि परिभाषित करने के बजाय cr आप उपयोग कर सकते हैं $'\n'। - devios1