मैं बैश में एक चर से लाइन द्वारा लाइन कैसे पढ़ सकता हूं?


48

मेरे पास एक वैरिएबल है जिसमें एक कमांड का मल्टीलाइन आउटपुट है। चर से लाइन द्वारा आउटपुट लाइन को पढ़ने का सबसे प्रभावी तरीका क्या है?

उदाहरण के लिए:

jobs="$(jobs)"
if [ "$jobs" ]; then
    # read lines from $jobs
fi

जवाबों:


64

आप प्रक्रिया प्रतिस्थापन के साथ थोड़ी देर के लूप का उपयोग कर सकते हैं:

while read -r line
do
    echo "$line"
done < <(jobs)

मल्टीलाइन चर को पढ़ने का एक इष्टतम तरीका एक रिक्त IFSचर और printfपरिवर्तनशील नई रेखा के साथ चर को सेट करना है :

# Printf '%s\n' "$var" is necessary because printf '%s' "$var" on a
# variable that doesn't end with a newline then the while loop will
# completely miss the last line of the variable.
while IFS= read -r line
do
   echo "$line"
done < <(printf '%s\n' "$var")

नोट: शेलचेक sc2031 के अनुसार, सबस्टेशन बनाने से बचने के लिए प्रक्रिया सबस्टेशन का उपयोग पाइप के लिए बेहतर है।

इसके अलावा, कृपया महसूस करें कि चर का नामकरण करने jobsसे भ्रम पैदा हो सकता है क्योंकि यह एक सामान्य शेल कमांड का नाम भी है।


3
यदि आप अपने सभी while IFS= readread -r
व्हाट्सएप

मैंने उन बिंदुओं को ठीक कर लिया है, जिनका उल्लेख किया गया है, साथ ही साथ बदल दिया गया echoहै printf %s, ताकि आपकी स्क्रिप्ट नॉन-टैम इनपुट के साथ भी काम करे।
गिल्स एसओ- बुराई को रोकना '

एक बहुस्तरीय चर से पढ़ने के लिए, एक हेरास्ट्रिंग को प्रिंटफ से पाइप करना बेहतर होता है (देखें l0b0 का उत्तर)।
ata

1
@ata हालांकि मैंने यह "बेहतर" सुना है अक्सर, यह ध्यान दिया जाना चाहिए कि एक हेरस्ट्रिंग को हमेशा /tmpनिर्देशिका को लिखने योग्य बनाने की आवश्यकता होती है , क्योंकि यह एक अस्थायी कार्य फ़ाइल बनाने में सक्षम होने पर निर्भर करता है। क्या आपको कभी भी खुद को /tmpकेवल पढ़ने के लिए एक प्रतिबंधित प्रणाली पर खोजना चाहिए (और आपके द्वारा परिवर्तनशील नहीं), आप वैकल्पिक समाधान का उपयोग करने की संभावना के बारे में खुश होंगे, जैसे कि printfपाइप के साथ ।
वाक्यविन्यास 22

1
दूसरे उदाहरण में, यदि मल्टी-लाइन चर में एक अनुगामी नई रेखा नहीं है, तो आप अंतिम तत्व को ढीला कर देंगे। इसे इसमें बदलें:printf "%s\n" "$var" | while IFS= read -r line
डेविड एच। बेनेट

26

लाइन ( स्पष्टीकरण ) द्वारा कमांड लाइन के आउटपुट को संसाधित करने के लिए :

jobs |
while IFS= read -r line; do
  process "$line"
done

यदि आपके पास पहले से ही एक चर में डेटा है:

printf %s "$foo" | 

printf %s "$foo"लगभग समान है echo "$foo", लेकिन $fooशाब्दिक रूप से प्रिंट करता है, जबकि गूंज कमांड के विकल्प के रूप में echo "$foo"व्याख्या कर सकता है $fooयदि यह एक के साथ शुरू होता है -, और $fooकुछ गोले में बैकलैश अनुक्रम का विस्तार कर सकता है ।

ध्यान दें कि कुछ गोले में (ऐश, बाश, पीडीएस, लेकिन केश या ज़श नहीं), पाइप लाइन का दाहिना हाथ एक अलग प्रक्रिया में चलता है, इसलिए लूप में आपके द्वारा सेट किया गया कोई भी चर खो जाता है। उदाहरण के लिए, निम्न पंक्ति-गणना स्क्रिप्ट इन शेल में 0 प्रिंट करता है:

n=0
printf %s "$foo" |
while IFS= read -r line; do
  n=$(($n + 1))
done
echo $n

एक $nआदेश सूची में शेष स्क्रिप्ट (या कम से कम उस भाग की आवश्यकता होती है जिसे लूप से मूल्य की आवश्यकता होती है ) डालते हैं :

n=0
printf %s "$foo" | {
  while IFS= read -r line; do
    n=$(($n + 1))
  done
  echo $n
}

यदि गैर-खाली लाइनों पर अभिनय करना काफी अच्छा है और इनपुट बहुत बड़ा नहीं है, तो आप शब्द विभाजन का उपयोग कर सकते हैं:

IFS='
'
set -f
for line in $(jobs); do
  # process line
done
set +f
unset IFS

स्पष्टीकरण: IFSएक ही नई पंक्ति पर सेट करने से शब्द विभाजन के रूप में नई सीमा पर ही होता है (डिफ़ॉल्ट सेटिंग के तहत किसी भी व्हाट्सएप चरित्र के विपरीत)। set -fग्लोबिंग (यानी वाइल्डकार्ड विस्तार) को बंद कर देता है, जो अन्यथा कमांड प्रतिस्थापन $(jobs)या एक चर सबस्टीट्यूटिनो के परिणाम के रूप में होता है $fooforलूप के सभी टुकड़ों पर काम करता है $(jobs), जो सभी आदेश उत्पादन में गैर खाली लाइनें हैं। अंत में, ग्लोबिंग और IFSसेटिंग्स को पुनर्स्थापित करें ।


मुझे IFS सेट करने और IFS को परेशान करने से परेशानी हुई है। मुझे लगता है कि सही काम करना IFS के पुराने मूल्य को संग्रहीत करना है और IFS को उस पुराने मूल्य पर वापस सेट करना है। मैं बैश विशेषज्ञ नहीं हूं, लेकिन मेरे अनुभव में, यह आपको मूल व्यवहार में वापस लाता है।
ब्योर्न रोश

1
@BjornRoche: किसी फ़ंक्शन के अंदर, उपयोग करें local IFS=something। यह वैश्विक-गुंजाइश मूल्य को प्रभावित नहीं करेगा। IIRC, unset IFSआपको डिफ़ॉल्ट पर वापस नहीं लाता है (और निश्चित रूप से यह काम नहीं करता है अगर यह पहले से डिफ़ॉल्ट नहीं था)।
पीटर कॉर्ड्स

14

समस्या: यदि आप लूप का उपयोग करते हैं तो यह सब-सब में चलेगा और सभी चर खो जाएंगे। समाधान: लूप के लिए उपयोग करें

# change delimiter (IFS) to new line.
IFS_BAK=$IFS
IFS=$'\n'

for line in $variableWithSeveralLines; do
 echo "$line"

 # return IFS back if you need to split new line by spaces:
 IFS=$IFS_BAK
 IFS_BAK=
 lineConvertedToArraySplittedBySpaces=( $line )
 echo "{lineConvertedToArraySplittedBySpaces[0]}"
 # return IFS back to newline for "for" loop
 IFS_BAK=$IFS
 IFS=$'\n'

done 

# return delimiter to previous value
IFS=$IFS_BAK
IFS_BAK=

बहुत बहुत धन्यवाद!! उपरोक्त सभी उपाय मेरे लिए विफल रहे।
hax0r_n_code

while readबैश में एक लूप में पाइपिंग का मतलब है जबकि लूप एक सब-उप में है, इसलिए चर वैश्विक नहीं हैं। while read;do ;done <<< "$var"लूप बॉडी को एक सबशेल नहीं बनाता है। (हाल के बैश में cmd | whileलूप के शरीर को एक उप-भाग में नहीं रखने का विकल्प होता है , जैसे ksh में हमेशा होता है।)
पीटर कॉर्ड्स


10

हाल के बैश संस्करणों में, कमांड आउटपुट को सरणियों में कुशलतापूर्वक पढ़ने mapfileया उपयोग readarrayकरने के लिए

$ readarray test < <(ls -ltrR)
$ echo ${#test[@]}
6305

डिस्क्लेमर: भयानक उदाहरण, लेकिन आप अपने आप को एलएस की तुलना में उपयोग करने के लिए बेहतर आदेश दे सकते हैं


यह एक अच्छा तरीका है, लेकिन मेरे सिस्टम पर अस्थायी फ़ाइलों के साथ litters / var / tmp। वैसे भी +1
यूजीन यर्मश

@ यूजीन: यह मज़ेदार है। क्या सिस्टम (डिस्ट्रो / ओएस) उस पर है?
सेह

यह FreeBSD है 8. कैसे पुन: पेश करें: readarrayएक फ़ंक्शन में डालें और फ़ंक्शन को कुछ बार कॉल करें।
यूजीन यर्मश

अच्छा लगा, @ हशे। +1
टेरेसा ई जूनियर


-1

आप <<< का उपयोग केवल चर से पढ़े जाने वाले न्यूलाइन-सेपरेट डेटा से कर सकते हैं:

while read -r line
do 
  echo "A line of input: $line"
done <<<"$lines"

1
यूनिक्स और लिनक्स में आपका स्वागत है! यह अनिवार्य रूप से चार साल पहले के एक उत्तर की नकल करता है। जब तक आपके पास योगदान करने के लिए कुछ नया न हो, कृपया उत्तर न दें।
जी-मैन का कहना है कि 'मोनिका'
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.