`IFS = के बजाय IFS = read` को इतनी बार क्यों उपयोग किया जाता है; पढ़े जाने के दौरान ..?


81

ऐसा लगता है कि सामान्य अभ्यास आईएफएस की सेटिंग को लूप के बाहर रखा जाएगा, ताकि प्रत्येक पुनरावृत्ति के लिए इसे दोहराए नहीं जा सके ... क्या यह सिर्फ एक आदतन "बंदर देखना, बंदर करना" शैली है, जैसा कि इस बंदर के लिए किया गया है मैंने पढ़ा आदमी पढ़ा है , या मैं कुछ सूक्ष्म (या स्पष्ट रूप से स्पष्ट) जाल यहाँ याद आ रही है?

जवाबों:


82

जाल वह है

IFS=; while read..

IFSलूप के बाहर पूरे शेल वातावरण के लिए सेट करता है , जबकि

while IFS= read

इसे केवल readआह्वान (बॉर्न शेल को छोड़कर) के लिए पुनर्परिभाषित करता है । आप जाँच कर सकते हैं कि जैसे कोई लूप कर रहा है

while IFS= read xxx; ... done

फिर ऐसे लूप के बाद, echo "blabalbla $IFS ooooooo"प्रिंट

blabalbla
 ooooooo

जबकि बाद में

IFS=; read xxx; ... done

IFS रहता है नए सिरे से परिभाषित: अब echo "blabalbla $IFS ooooooo"प्रिंट

blabalbla  ooooooo

इसलिए यदि आप दूसरे फॉर्म का उपयोग करते हैं, तो आपको रीसेट करना याद रखना होगा IFS=$' \t\n':।


इस प्रश्न का दूसरा भाग यहाँ विलय कर दिया गया है , इसलिए मैंने संबंधित उत्तर को यहाँ से हटा दिया है।


ठीक है, ऐसा लगता है कि एक संभावित 'ट्रैप' बाहरी IFS को रीसेट करने के लिए उपेक्षा करने के लिए है ... लेकिन मुझे आश्चर्य है कि अगर कुछ और भी हो रहा है तो ... मैं यहां चीजों का परीक्षण कर रहा हूं, बहुत बुखार से, और मैंने ध्यान दें कि IFS सेट करते समय कमांड लिस्ट में अलग-अलग तरीके से qute का व्यवहार किया जाता है, जो इस बात पर निर्भर करता है कि यह एक कोलन द्वारा अनुसरण किया गया है या नहीं। मैं इस व्यवहार (अभी तक) को नहीं समझता हूं, और मुझे आश्चर्य है कि क्या इस स्तर पर कुछ विशेष विचार शामिल हैं ... उदाहरण के लिए। while IFS=X readपर विभाजित नहीं करता है X, लेकिन while IFS=X; read...
पीटर।

(आप अर्ध उपनिवेश का मतलब है, ठीक है?) दूसरा whileमतलब नहीं है - उस अर्धविराम पर while समाप्त होने की स्थिति है , इसलिए कोई वास्तविक लूप नहीं है ... readएक-तत्व लूप के अंदर सिर्फ पहला कमांड बन जाता है ... या नहीं ? doतब के बारे में क्या ..?
रोज़ज़ेट्राइजेविज़

1
नहीं, प्रतीक्षा करें - आप सही हैं, आपके पास whileस्थिति (पहले do) में कई कमांड हो सकते हैं ।
रोज़ज़ेट्राइजेविज़

ओह .. निश्चित रूप से, आप उनके पास हो सकते हैं ... जैसा कि आपने महसूस किया है ... लेकिन वे अर्ध-कोलन की तरह नहीं लगते हैं ... (और लूप ऐड-इनफिनिटम को लूप करते रहेंगे, जब तक कि अंतिम कमांड नॉन न लौटे -zero से बाहर निकलें कोड) ... मैं अब सोच रहा था कि जाल पूरी तरह से एक अलग क्षेत्र में है; यह समझते हुए कि कमांड लिस्ट कैसे काम करती है, जैसे। क्यों IFS=काम करता है , लेकिन IFS=Xनहीं ... (या हो सकता है कि मैंने इस पर थोड़ी देर के लिए ODD किया था .. कॉफी ब्रेक की जरूरत :)
पीटर।

1
$ rozcietrzewiacz .. ओह ... मैं अपने अद्यतन, गौर नहीं किया था जब मैं अपने अद्यतन चले गए (पिछला टिप्पणी में उल्लेख किया) .. यह दिलचस्प लग रहा है, और यह है शुरू कर समझ बनाने के लिए ... लेकिन यहां तक कि एक रात के लिए मेरे जैसे पक्षी, यह बहुत देर हो चुकी है ... (मैंने अभी सुबह पक्षियों को सुना है:) ... ने कहा, मैंने थोड़ा रुला दिया है और आपके उदाहरण पढ़ता हूं ... मुझे लगता है कि मुझे मिल गया है, वास्तव में मैं ' मुझे यकीन है कि आपको मिल गया है, लेकिन मुझे सोना होगा :) ... यह लगभग एक यूरेका है! पल ... धन्यवाद
पीटर।

45

आइए एक उदाहरण देखें, कुछ ध्यान से तैयार किए गए इनपुट टेक्स्ट के साथ:

text=' hello  world\
foo\bar'

यह दो पंक्तियाँ हैं, पहली शुरुआत अंतरिक्ष से होती है और एक बैकस्लैश के साथ समाप्त होती है। पहले, आइए देखें कि बिना किसी सावधानी के क्या होता है read(लेकिन विस्तार के किसी भी जोखिम के बिना printf '%s\n' "$text"सावधानीपूर्वक प्रिंट करने के लिए $text)। (नीचे, $ ‌शेल प्रॉम्प्ट है।)

$ printf '%s\n' "$text" |
  while read line; do printf '%s\n' "[$line]"; done
[hello worldfoobar]

readबैकस्लैश खा लिया: बैकस्लैश-न्यूलाइन की वजह से न्यूलाइन को अनदेखा किया जा सकता है, और बैकस्लैश-कुछ भी पहले बैकस्लैश को अनदेखा करता है। विशेष रूप से इलाज किए जा रहे बैकस्लैश से बचने के लिए, हम उपयोग करते हैं read -r

$ printf '%s\n' "$text" |
  while read -r line; do printf '%s\n' "[$line]"; done
[hello  world\]
[foo\bar]

यह बेहतर है, हमारे पास उम्मीद के मुताबिक दो लाइनें हैं। दो पंक्तियों में लगभग वांछित सामग्री होती है: इसके बीच का दोहरा स्थान helloऔर worldबरकरार रखा गया है, क्योंकि यह lineचर के भीतर है । दूसरी ओर, प्रारंभिक स्थान खा लिया गया था। ऐसा इसलिए readहै क्योंकि आप इसे चर के रूप में कई शब्दों के रूप में पढ़ता है, सिवाय इसके कि अंतिम चर में बाकी पंक्ति होती है - लेकिन यह अभी भी पहले शब्द से शुरू होता है, अर्थात प्रारंभिक स्थान छोड़ दिए जाते हैं।

इसलिए, प्रत्येक पंक्ति को शाब्दिक रूप से पढ़ने के लिए, हमें यह सुनिश्चित करने की आवश्यकता है कि कोई शब्द विभाजन नहीं हो रहा है। हम IFSचर को एक खाली मान पर सेट करके ऐसा करते हैं ।

$ printf '%s\n' "$text" |
  while IFS= read -r line; do printf '%s\n' "[$line]"; done
[ hello  world\]
[foo\bar]

ध्यान दें कि हम IFS विशेष रूप से readअंतर्निहित अवधि के लिए कैसे सेट करते हैंIFS= read -r lineसेट वातावरण चर IFSविशेष रूप से के निष्पादन के लिए (एक खाली मूल्य के लिए) read। यह सामान्य सरल कमांड सिंटैक्स का एक उदाहरण है : चर असाइनमेंट का अनुक्रम (संभवतः खाली) एक कमांड नाम और उसके तर्क (इसके बाद भी, आप किसी भी बिंदु पर पुनर्निर्देशन में फेंक सकते हैं)। चूंकि readएक अंतर्निर्मित है, चर वास्तव में बाहरी प्रक्रिया के वातावरण में समाप्त नहीं होता है; फिर भी मूल्य $IFSहै कि जब तक हम readनिष्पादित कर रहे हैं, तब तक हम क्या कर रहे हैं । ध्यान दें कि readएक विशेष अंतर्निहित नहीं है , इसलिए असाइनमेंट केवल इसकी अवधि के लिए रहता है।

इस प्रकार हम इस बात पर ध्यान दे रहे हैं IFSकि उस पर निर्भर अन्य निर्देशों के मूल्य को न बदलें । यह कोड किसी भी तरह से काम नहीं करेगा जो कि आसपास के कोड ने IFSशुरू में सेट किया है, और यह किसी भी परेशानी का कारण नहीं होगा यदि लूप के अंदर का कोड निर्भर करता है IFS

इस कोड स्निपेट के साथ विरोध करें, जो एक बृहदान्त्र-पृथक पथ में फ़ाइलों को देखता है। फ़ाइल नामों की सूची एक फ़ाइल, प्रति पंक्ति एक फ़ाइल नाम से पढ़ी जाती है।

IFS=":"; set -f
while IFS= read -r name; do
  for dir in $PATH; do
    ## At this point, "$IFS" is still ":"
    if [ -e "$dir/$name" ]; then echo "$dir/$name"; fi
  done
done <filenames.txt

यदि लूप होता while IFS=; read -r name; do …, तो बृहदान्त्र-पृथक घटकों में for dir in $PATHविभाजित नहीं होता $PATH। यदि कोड था IFS=; while read …, तो यह और भी स्पष्ट होगा कि लूप बॉडी में IFSसेट नहीं :है।

बेशक, IFSनिष्पादित करने के बाद मूल्य को बहाल करना संभव होगा read। लेकिन इसके लिए पिछले मूल्य को जानने की आवश्यकता होगी, जो अतिरिक्त प्रयास है। IFS= readसरल तरीका है (और, सुविधाजनक रूप से, सबसे छोटा तरीका भी)।

¹ और, यदि readफंसे सिग्नल द्वारा बाधित किया जाता है, संभवतः जब जाल निष्पादित हो रहा है - यह POSIX द्वारा निर्दिष्ट नहीं है और अभ्यास में शेल पर निर्भर करता है।


4
धन्यवाद गिल्स .. एक बहुत अच्छा निर्देशित दौरा .. (क्या आपका मतलब 'सेट -f'?) .... अब, पाठक के लिए, जो पहले से ही कहा गया है, उसे आराम करने के लिए, मैं उस मुद्दे पर जोर देना चाहूंगा जो था मैं इसे गलत तरीके से देख रहा हूं। सबसे पहले तथ्य यह है कि निर्माण है while IFS= read(एक सेमी-कोलन के बाद बिना =) है की एक विशेष रूप whileया की IFSया की readयानी: .. निर्माण सामान्य है। anyvar=anyvalue anycommand;सेटिंग के बाद की कमी स्थानीय का anyvarदायरा बनाती है .. जबकि - किया / किया हुआ लूप स्थानीय दायरे से 100% असंबंधित होता है । anyvar anycommandany_var
पीटर।

3

(और पहले से स्पष्ट) IFSस्कूप अंतर के अलावा while IFS='' read, IFS=''; while readऔर while IFS=''; readमुहावरों (प्रति-कमांड बनाम स्क्रिप्ट / शेल-वाइड IFSवैरिएबल स्कूपिंग) के अलावा, टेक-होम सबक यह है कि आप इनपुट लाइन के प्रमुख और अनुगामी रिक्त स्थान खो देते हैं यदि IFS चर के लिए (एक) स्थान निर्धारित है।

यदि फ़ाइल पथ संसाधित किए जा रहे हैं तो इसके बहुत गंभीर परिणाम हो सकते हैं।

इसलिए IFS वैरिएबल को खाली स्ट्रिंग पर सेट करना कुछ भी है लेकिन एक बुरा विचार है क्योंकि यह सुनिश्चित करता है कि एक लाइन के अग्रणी और पीछे वाले व्हाट्सएप को छीन नहीं लिया जाए।

इसे भी देखें: बैश, लाइन से लाइन को फ़ाइल से पढ़ें, IFS के साथ

(
shopt -s nullglob
touch '  file with spaces   '
IFS=$' \t\n' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
IFS='' read -r file <<<"$(printf '%s' *file*with*spaces*)"
ls -l "$file"
)

+1 उत्कृष्ट प्रदर्शन, 'रिक्त स्थान * * के साथ * रिक्त स्थान *' के साथ सफाई
amdn

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.