"IFS = रीड-आर लाइन" को समझना


60

मैं स्पष्ट रूप से समझता हूं कि कोई व्यक्ति आंतरिक क्षेत्र विभाजक चर में मूल्य जोड़ सकता है। उदाहरण के लिए:

$ IFS=blah
$ echo "$IFS"
blah
$ 

मैं यह भी समझता हूं कि नामांकित चर read -r lineसे डेटा को बचाएगा :stdinline

$ read -r line <<< blah
$ echo "$line"
blah
$ 

हालांकि, एक कमांड वेरिएबल वैल्यू को कैसे असाइन कर सकता है? और क्या यह पहले stdinवेरिएबल से डेटा स्टोर करता है lineऔर फिर वैल्यू ऑफ देता lineहै IFS?


3
सम्बंधित: unix.stackexchange.com/q/169716/38906
cuonglm

जवाबों:


104

कुछ लोगों के पास वह गलत धारणा है जो readएक पंक्ति को पढ़ने की आज्ञा है। यह।

read(संभवतः बैकस्लैश-कंटिन्यूड) लाइन के शब्दों को पढ़ता है , जहाँ $IFSसीमांकित शब्दों को सीमांकित किया जाता है और सीमांकक (या लाइन्स जारी रखने) से बचने के लिए बैकस्लैश का उपयोग किया जा सकता है।

सामान्य वाक्यविन्यास है:

read word1 word2... remaining_words

readएक समय में stdin एक बाइट पढ़ता है जब तक यह पाता है कोई अनपेक्षित न्यू लाइन चरित्र (या अंत के इनपुट), विभाजन है कि बंटवारे के परिणाम में जटिल नियमों और दुकानों के अनुसार $word1, $word2... $remaining_words

उदाहरण के लिए इनपुट पर:

  <tab> foo bar\ baz   bl\ah   blah\
whatever whatever

और का डिफ़ॉल्ट मान के साथ $IFS, read a b cआवंटित होगा:

  • $afoo
  • $bbar baz
  • $cblah blahwhatever whatever

अब अगर केवल एक ही तर्क पारित किया गया, तो वह नहीं बनता read line। यह अभी भी है read remaining_words। बैकस्लैश प्रसंस्करण अभी भी किया जाता है, IFS व्हाट्सएप वर्ण अभी भी शुरुआत और अंत से हटा दिए गए हैं।

-rविकल्प बैकस्लैश प्रसंस्करण निकाल देता है। ताकि इसके साथ ही ऊपर के आदेश के -rबजाय असाइन किया जाएगा

  • $afoo
  • $bbar\
  • $cbaz bl\ah blah\

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

साथ IFS=:( :नहीं एक आईएफएस खाली स्थान के चरित्र जा रहा है), जैसा एक इनपुट :foo::bar::भागों में विभाजित किया "", "foo", "", barऔर ""(और एक अतिरिक्त ""कुछ कार्यान्वयन के साथ कि हालांकि के अलावा कोई फर्क नहीं पड़ता read -a)। जबकि अगर हम इसे :अंतरिक्ष से प्रतिस्थापित करते हैं, तो विभाजन केवल fooऔर में किया जाता है bar। यह प्रमुख है और अनुगामी लोगों की उपेक्षा की जाती है, और उनमें से दृश्यों को एक जैसा माना जाता है। व्हॉट्सएप और नॉन-व्हॉट्सएप कैरेक्टर संयुक्त होने पर अतिरिक्त नियम हैं $IFS। कुछ कार्यान्वयन आईएफएस ( IFS=::या IFS=' ') में पात्रों को दोगुना करके विशेष उपचार को जोड़ / हटा सकते हैं ।

इसलिए, यदि हम नहीं चाहते कि अग्रणी और अनुत्तरित व्हाट्सएप वर्णों को छीन लिया जाए, तो हमें आईएफएस से उन IFS सफेद अंतरिक्ष वर्णों को हटाने की आवश्यकता है।

यहां तक कि भारतीय विदेश सेवा-गैर-सफ़ेद पात्रों के साथ, इनपुट लाइन उन पात्रों में से एक (और केवल एक) होता है और यह (जैसे लाइन के अंतिम वर्ण है कि अगर IFS=: read -r wordकी तरह एक इनपुट पर foo:) POSIX गोले (नहीं के साथ zshहै और न ही कुछ pdkshसंस्करण), कि इनपुट एक fooशब्द के रूप में माना जाता है क्योंकि उन गोले में, वर्णों $IFSको टर्मिनेटर माना जाता है , इसलिए wordइसमें शामिल होगा foo, नहीं foo:

तो, readबेसिन के साथ इनपुट की एक पंक्ति को पढ़ने के लिए विहित तरीका है:

IFS= read -r line

(ध्यान दें कि ज्यादातर readकार्यान्वयन के लिए, यह केवल पाठ लाइनों के लिए काम करता है क्योंकि NUL वर्ण को छोड़कर समर्थित नहीं है zsh)।

var=value cmdवाक्यविन्यास का उपयोग करना सुनिश्चित करता है कि IFSकेवल उस cmdकमांड की अवधि के लिए अलग-अलग सेट किया गया है ।

इतिहास नोट

readBuiltin बॉर्न शैल द्वारा शुरू की और पढ़ने के लिए पहले से ही किया गया था शब्द , नहीं लाइनों। आधुनिक POSIX गोले के साथ कुछ महत्वपूर्ण अंतर हैं।

बॉर्न शेल readने एक -rविकल्प का समर्थन नहीं किया (जो कोर्न शेल द्वारा पेश किया गया था), इसलिए वहाँ कुछ के साथ इनपुट को पूर्व-प्रसंस्करण के अलावा बैकस्लैश प्रसंस्करण को अक्षम करने का कोई तरीका नहीं है sed 's/\\/&&/g'

बॉर्न शेल में वर्णों की दो वर्गों की धारणा नहीं थी (जो फिर से ksh द्वारा पेश की गई थी)। बॉर्न में शेल सभी पात्रों को एक ही उपचार कराने के रूप में भारतीय विदेश सेवा खाली स्थान के पात्रों ksh में करते हैं, वह यह है कि IFS=: read a b cजैसा एक इनपुट पर foo::barआवंटित होगा barकरने के लिए $b, नहीं रिक्त स्ट्रिंग।

बॉर्न शेल में, के साथ:

var=value cmd

यदि cmdएक अंतर्निहित (जैसे read) है, तो समाप्त होने के बाद varसेट रहता है । यह विशेष रूप से महत्वपूर्ण है क्योंकि बॉर्न शेल में, विस्तार करने के लिए ही नहीं, सब कुछ विभाजित करने के लिए उपयोग किया जाता है। इसके अलावा, यदि आप बॉर्न शेल में स्पेस कैरेक्टर को हटाते हैं, तो काम नहीं करता है।valuecmd$IFS$IFS$IFS"$@"

बॉर्न शेल में, एक कंपाउंड कमांड को रीडायरेक्ट करने के कारण यह सबस्क्रिप्शन (सबसे शुरुआती संस्करणों में, यहां तक ​​कि जैसी चीजें read var < fileया exec 3< file; read var <&3काम नहीं करता) में चलने का कारण बनता है , इसलिए बॉर्न शेल readमें टर्मिनल पर उपयोगकर्ता इनपुट के लिए कुछ भी उपयोग करना दुर्लभ था (जहां उस लाइन निरंतरता से निपटने की भावना बनी)

कुछ यूनियनों (जैसे कि एचपी / यूएक्स, वहां भी एक है util-linux) में अभी भी lineइनपुट की एक पंक्ति को पढ़ने के लिए एक कमांड है (जो कि एकल यूनिक्स विशिष्टता संस्करण 2 तक एक मानक यूनिक्स कमांड हुआ करता था )।

यह मूल रूप से head -n 1सिवाय इसके कि यह एक बार में एक बाइट पढ़ता है, यह सुनिश्चित करने के लिए कि यह एक पंक्ति से अधिक नहीं पढ़ता है। उन प्रणालियों पर, आप यह कर सकते हैं:

line=`line`

बेशक, इसका मतलब है कि एक नई प्रक्रिया पैदा करना, एक कमांड निष्पादित करना और एक पाइप के माध्यम से इसके आउटपुट को पढ़ना, ताकि ksh की तुलना में बहुत कम कुशल हो IFS= read -r line, लेकिन अभी भी बहुत अधिक सहज है।


3
+1 bash में IFS में स्पेस / टैब बनाम "अन्य" पर अलग-अलग उपचारों की कुछ उपयोगी अंतर्दृष्टि के लिए धन्यवाद ... मुझे पता था कि उनके साथ अलग तरह से व्यवहार किया गया था, लेकिन यह स्पष्टीकरण यह सब बहुत सरल करता है। (और बैश (और अन्य पॉज़िक्स गोले के बीच की अंतर्दृष्टि) और नियमित shअंतर पोर्टेबल स्क्रिप्ट लिखने के लिए बहुत उपयोगी है!)
ओलिवियर दुलैक

कम से कम bash-4.4.19, के while read -r; do echo "'$REPLY'"; doneरूप में काम करता है while IFS= read -r line; do echo "'$line'"; done
x- यूरी

यह: "... वह त्रुटिपूर्ण धारणा जो पढ़ी जाती है वह एक पंक्ति पढ़ने की आज्ञा है ..." मुझे सोचने के लिए प्रेरित करती है, कि यदि readकिसी पंक्ति को पढ़ने के लिए उपयोग गलत है, तो कुछ और होना चाहिए। वह गैर-गलत धारणा क्या हो सकती है? या यह कि पहले बयान तकनीकी रूप से सही है, लेकिन वास्तव में गैर-गलत धारणा है: "रीड एक पंक्ति से शब्दों को पढ़ने की आज्ञा है। क्योंकि यह बहुत शक्तिशाली है, आप इसे किसी फ़ाइल से लाइनों को पढ़ने के लिए उपयोग कर सकते हैं: IFS= read -r line"
माइक एस

8

सिद्धांत

दो अवधारणाएं हैं जो यहां चल रही हैं:

  • IFSइनपुट फ़ील्ड सेपरेटर है, जिसका अर्थ है कि स्ट्रिंग रीड को वर्णों के आधार पर विभाजित किया जाएगा IFS। कमांड लाइन पर, IFSसामान्यतः कोई भी व्हाट्सएप वर्ण होता है, इसीलिए कमांड लाइन रिक्त स्थान पर विभाजित होती है।
  • कुछ ऐसा VAR=value commandकरने का अर्थ है "कमांड के वातावरण को संशोधित करें ताकि VARमूल्य हो value"। मूल रूप से, कमांड मान के रूप में commandदेखेगा , लेकिन उसके बाद निष्पादित कोई भी कमांड अभी भी अपने पिछले मूल्य के रूप में देखेगा । दूसरे शब्दों में, उस चर को केवल उस कथन के लिए संशोधित किया जाएगा।VARvalueVAR

इस मामले में

इसलिए जब IFS= read -r lineआप कर रहे हैं, तो IFSएक खाली स्ट्रिंग पर सेट हो रहा है (कोई वर्ण विभाजित करने के लिए उपयोग नहीं किया जाएगा, इसलिए कोई विभाजन नहीं होगा) ताकि readपूरी पंक्ति को पढ़े और उसे एक शब्द के रूप में देखें जो कि lineचर को सौंपा जाएगा । परिवर्तन IFSकेवल उस कथन को प्रभावित करते हैं, ताकि परिवर्तन के बाद कोई भी आदेश प्रभावित न हो।

अलग नोट के रूप में

जबकि आदेश सही है और इरादा के रूप में काम करेंगे, की स्थापना IFSइस मामले में नहीं है हो सकता है 1 नहीं हो आवश्यक। जैसा कि bashमैन पेज में readबेसिन अनुभाग में लिखा गया है :

मानक इनपुट [...] से एक पंक्ति पढ़ी जाती है और पहला शब्द पहले नाम को सौंपा जाता है, दूसरा शब्द दूसरे नाम को, और इसी तरह, बचे हुए शब्दों और उनके बीच के विभाजक को अंतिम नाम को सौंपा जाता है । यदि नामों की तुलना में इनपुट स्ट्रीम से कम शब्द पढ़े जाते हैं, तो शेष नामों को खाली मान दिए जाते हैं। वर्णों IFSको शब्दों में रेखा को विभाजित करने के लिए उपयोग किया जाता है। [...]

चूंकि आपके पास केवल lineचर है, वैसे भी हर शब्द इसे सौंपा जाएगा, इसलिए यदि आपको पूर्ववर्ती और अनुगामी व्हॉट्सएप के किसी भी चरित्र की आवश्यकता नहीं है 1 तो आप बस लिख सकते हैं read -r lineऔर इसके साथ किया जा सकता है।

[१] IFS व्हाट्सएप के अग्रणी / अनुगामी के संबंध में कैसे unsetया डिफ़ॉल्ट $IFSमान के कारण उदाहरण के रूप में , आप निम्नलिखित हो सकते हैं:read

echo ' where are my spaces? ' | { 
    unset IFS
    read -r line
    printf %s\\n "$line"
} | sed -n l

इसे चलाएं और आप देखेंगे कि पूर्ववर्ती और अनुगामी अक्षर जीवित नहीं रहेंगे यदि IFSकोई परेशान नहीं है। इसके अलावा, कुछ अजीब चीजें हो सकती हैं अगर $IFSस्क्रिप्ट में पहले कहीं संशोधित किया जाना था।


5

आप दो भागों में है कि बयान पढ़ना चाहिए, पहले एक आईएफएस चर के मूल्य को साफ करता है, यानी अधिक पठनीय के बराबर है IFS="", दूसरा एक पढ़ रही है line, stdin से चर read -r line

इस सिंटैक्स में जो विशिष्ट है वह है IFS प्रभाव ट्रांसजेंडर और केवल readकमांड के लिए मान्य है ।

जब तक मैं कुछ याद नहीं कर रहा हूं, उस विशेष मामले में समाशोधन IFSका कोई प्रभाव नहीं है, क्योंकि जो कुछ भी IFSसेट किया गया है, पूरी पंक्ति को lineचर में पढ़ा जाएगा । केवल readनिर्देश के पैरामीटर के रूप में एक से अधिक चर पास होने की स्थिति में व्यवहार में बदलाव होता ।

संपादित करें:

-rइनपुट के साथ समाप्त होने की अनुमति देने के वहाँ \नहीं, विशेष रूप से संसाधित करने के लिए अर्थात् बैकस्लैश में शामिल किया जाना lineनहीं एक निरंतरता चरित्र बहु लाइन इनपुट अनुमति देने के लिए के रूप में चर और।

$ read line; echo "[$line]"   
abc\
> def
[abcdef]
$ read -r line; echo "[$line]"  
abc\
[abc\]

समाशोधन IFS के संभावित अग्रणी और अनुगामी स्थान या टैब वर्णों को ट्रिम करने के लिए रीड को रोकने का दुष्प्रभाव है, जैसे:

$ echo "   a b c   " | { IFS= read -r line; echo "[$line]" ; }   
[   a b c   ]
$ echo "   a b c   " | { read -r line; echo "[$line]" ; }     
[a b c]

उस अंतर को इंगित करने के लिए रिसीक के लिए धन्यवाद।


आप जो याद कर रहे हैं वह यह है कि यदि IFS नहीं बदला गया है, read -r lineतो lineचर को इनपुट देने से पहले प्रमुख और अनुगामी व्हाट्सएप को ट्रिम कर देगा ।
रिसी

@rici मुझे ऐसा कुछ संदेह था, लेकिन केवल शब्दों के बीच IFS वर्णों की जाँच की, न कि प्रमुख / अनुगामी। उस तथ्य को इंगित करने के लिए धन्यवाद!
जुलियाग्रे

समाशोधन IFS कई चर (साइड इफेक्ट) के कार्य को भी रोकेगा। IFS= read a b <<< 'aa bb' ; echo "-$a-$b-"दिखाएंगे-aa bb--
kyodev
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.