यह सिर्फ इको बनाम प्रिंटफ नहीं है
पहले, आइए समझते हैं कि 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()
भाषा में सी भाषा में जो कार्य करता है उससे बेहतर व्यवहार के रूप में मौजूद है ।
आपने यहां भी क्या किया जो आउटपुट को प्रभावित करता है वह यह है कि आपके चर को उद्धृत नहीं किया जाता है, जो शेल ( printf
6) को अलग-अलग 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()
कार्य करता है। बनाम के विषय पर टेराडन द्वारा यह उत्कृष्ट उत्तर देखें । जब आप उबंटू के अपने विशिष्ट संस्करण पर अपने विशिष्ट शेल के अनुरूप आउटपुट कर सकते हैं, यदि आप विभिन्न प्रणालियों में स्क्रिप्ट को पोर्ट करने जा रहे हैं, तो आपको संभवतः प्रतिध्वनि के बजाय पसंद करना चाहिए । हो सकता है कि आप उबंटू और सेंटोस मशीनों के साथ काम करने वाले एक शुरुआती सिस्टम प्रशासक हों, या शायद फ्रीबीएसडी - जो जानते हैं - इसलिए ऐसे मामलों में आपको विकल्प बनाने होंगे।printf
echo
printf
बैश मैनुअल से उद्धरण, शेल बिल्डिन कमैंट्स सेक्शन
पढ़ें [-अर्स] [-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
समय प्रारूपण के लिए।