यह सिर्फ इको बनाम प्रिंटफ नहीं है
पहले, आइए समझते हैं कि read a b cभाग के साथ क्या होता है । वैरिएबल readके डिफ़ॉल्ट मान के आधार पर वर्ड-स्प्लिटिंग करेगा IFSजो स्पेस-टैब-न्यूलाइन है, और उसके आधार पर सब कुछ फिट होगा। यदि इसे धारण करने के लिए चर की तुलना में अधिक इनपुट है, तो यह पहले चर में विभाजित भागों में फिट होगा, और जो फिट नहीं किया जा सकता है - वह अंतिम में जाएगा। यहाँ मेरा मतलब है:
bash-4.3$ read a b c <<< "one two three four"
bash-4.3$ echo $a
one
bash-4.3$ echo $b
two
bash-4.3$ echo $c
three four
यह वास्तव में इसका वर्णन किस प्रकार से किया गया है bash(उत्तर के अंत में उद्धरण देखें)।
आपके मामले में ऐसा नहीं होता है, 1 और 2 एक और बी चर में फिट होते हैं, और सी सब कुछ लेता है, जो है 3 4 5 6।
आप भी कई बार देखेंगे कि लोग while IFS= read -r line; do ... ; done < input.txtटेक्स्ट फाइल को लाइन से पढ़ने के लिए उपयोग करते हैं। फिर, IFS=यहाँ शब्द-विभाजन को नियंत्रित करने के लिए, या अधिक विशेष रूप से यहाँ है - इसे अक्षम करें, और एक चर में पाठ की एक पंक्ति पढ़ें। यदि यह नहीं था, readतो प्रत्येक व्यक्ति शब्द को lineचर में फिट करने की कोशिश की जाएगी । लेकिन यह एक और कहानी है, जिसे मैं आपको बाद में अध्ययन करने के लिए प्रोत्साहित करता हूं, क्योंकि while IFS= read -r variableयह एक बहुत ही अक्सर उपयोग की जाने वाली संरचना है।
इको बनाम प्रिंटफ़ व्यवहार
echoक्या आप यहाँ उम्मीद करेंगे यह आपके चरों को ठीक उसी तरह प्रदर्शित करता है जैसे readउन्हें व्यवस्थित किया है। पिछली चर्चा में इसका प्रदर्शन किया जा चुका है।
printfबहुत विशेष है, क्योंकि यह सभी को समाप्त होने तक प्रारूप चर में फिटिंग चर पर रखेगा। इसलिए जब आप printf "%d, %d, %d \n" $a $b $cप्रिंटफ को 3 दशमलव के साथ प्रारूप स्ट्रिंग देखते हैं, लेकिन 3 से अधिक तर्क हैं (क्योंकि आपके चर वास्तव में व्यक्तिगत 1,2,3,4,5,6 तक विस्तारित हैं)। यह भ्रामक लग सकता है, लेकिन वास्तविक printf() भाषा में सी भाषा में जो कार्य करता है उससे बेहतर व्यवहार के रूप में मौजूद है ।
आपने यहां भी क्या किया जो आउटपुट को प्रभावित करता है वह यह है कि आपके चर को उद्धृत नहीं किया जाता है, जो शेल ( printf6) को अलग-अलग 6 वस्तुओं में तोड़ने की अनुमति नहीं देता है। इसकी तुलना उद्धरण के साथ करें:
bash-4.3$ read a b c <<< "1 2 3 4"
bash-4.3$ printf "%d %d %d\n" "$a" "$b" "$c"
bash: printf: 3 4: invalid number
1 2 3
बिल्कुल $cवैसा ही क्योंकि चर को उद्धृत किया जाता है, इसे अब एक पूरे स्ट्रिंग के रूप में पहचाना जाता है 3 4, और यह %dप्रारूप में फिट नहीं होता है , जो सिर्फ एक पूर्णांक है
अब बिना उद्धृत किए भी ऐसा ही करें:
bash-4.3$ printf "%d %d %d\n" $a $b $c
1 2 3
4 0 0
printf फिर से कहता है: "ठीक है, आपके पास 6 आइटम हैं, लेकिन प्रारूप केवल 3 दिखाता है, इसलिए मैं फिटिंग सामान रखूंगा और खाली छोड़ दूंगा जो मैं उपयोगकर्ता से वास्तविक इनपुट से मेल नहीं खा सकता"।
और इन सभी मामलों में आपको इसके लिए मेरा शब्द नहीं लेना पड़ेगा। बस चलाएं strace -e trace=execveऔर अपने आप को देखें कि वास्तव में कमांड "देखना" क्या है:
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" $a $b $c
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3", "4"], [/* 80 vars */]) = 0
1 2 3
4 0 0
+++ exited with 0 +++
bash-4.3$ strace -e trace=execve printf "%d %d %d\n" "$a" "$b" "$c"
execve("/usr/bin/printf", ["printf", "%d %d %d\\n", "1", "2", "3 4"], [/* 80 vars */]) = 0
1 2 printf: ‘3 4’: value not completely converted
3
+++ exited with 1 +++
अतिरिक्त नोट्स
जैसा कि चार्ल्स डफी ने ठीक से टिप्पणियों में बताया है, bashइसका अपना बिल्ट-इन है printf, जिसे आप अपने कमांड में इस्तेमाल कर रहे हैं, straceवास्तव में /usr/bin/printfवर्जन को कॉल करेगा , शेल का वर्जन नहीं। मामूली अंतर के अलावा, इस विशेष प्रश्न में हमारी रुचि के लिए मानक प्रारूप विनिर्देशक समान हैं और व्यवहार समान है।
यह भी ध्यान में रखा जाना चाहिए कि printfवाक्यविन्यास कहीं अधिक पोर्टेबल (और इसलिए पसंदीदा) की तुलना में है echo, यह उल्लेख करने के लिए नहीं कि वाक्यविन्यास सी या किसी भी सी-जैसी भाषा से परिचित है जो इसमें printf()कार्य करता है। बनाम के विषय पर टेराडन द्वारा यह उत्कृष्ट उत्तर देखें । जब आप उबंटू के अपने विशिष्ट संस्करण पर अपने विशिष्ट शेल के अनुरूप आउटपुट कर सकते हैं, यदि आप विभिन्न प्रणालियों में स्क्रिप्ट को पोर्ट करने जा रहे हैं, तो आपको संभवतः प्रतिध्वनि के बजाय पसंद करना चाहिए । हो सकता है कि आप उबंटू और सेंटोस मशीनों के साथ काम करने वाले एक शुरुआती सिस्टम प्रशासक हों, या शायद फ्रीबीएसडी - जो जानते हैं - इसलिए ऐसे मामलों में आपको विकल्प बनाने होंगे।printfechoprintf
बैश मैनुअल से उद्धरण, शेल बिल्डिन कमैंट्स सेक्शन
पढ़ें [-अर्स] [-a aname] [-d delim] [-i पाठ] [-n nchars] [-N nchars] [-p प्रॉम्प्ट] [-t timeout] [-u fd] [नाम ... ]
एक लाइन मानक इनपुट से पढ़ी जाती है, या फ़ाइल डिस्क्रिप्टर से -d विकल्प के तर्क के रूप में आपूर्ति की जाती है, और पहला शब्द पहले नाम को सौंपा जाता है, दूसरा शब्द दूसरे नाम को, और इसी तरह, बचे हुए के साथ शब्द और उनके बीच का विभाजक अंतिम नाम को सौंपा गया है। यदि नामों की तुलना में इनपुट स्ट्रीम से कम शब्द पढ़े जाते हैं, तो शेष नामों को खाली मान दिए जाते हैं। IFS के वर्णों का उपयोग शब्दों को लाइन में विभाजित करने के लिए किया जाता है, उसी नियमों का उपयोग करके जो शेल विस्तार के लिए उपयोग करता है (वर्ड स्प्लिटिंग के तहत ऊपर वर्णित है)।
straceमामले और दूसरे के बीच एक उल्लेखनीय अंतर -strace printfउपयोग कर रहा है/usr/bin/printf, जबकिprintfसीधे बैश में उसी नाम से शेल बिलिन का उपयोग कर रहा है। वे हमेशा समान नहीं होंगे - उदाहरण के लिए, बैश उदाहरण में प्रारूप विनिर्देशक हैं%qऔर नए संस्करणों में,$()Tसमय प्रारूपण के लिए।