“पढ़ते समय…” का उपयोग करते हुए, गूंज और प्रिंटफ को अलग-अलग परिणाम मिलते हैं


14

इस सवाल के अनुसार "एक लिपिक्स स्क्रिप्ट में" पढ़ते समय ... "का उपयोग करना "

echo '1 2 3 4 5 6' | while read a b c;do echo "$a, $b, $c"; done

परिणाम:

1, 2, 3 4 5 6

लेकिन जब मैं के echoसाथ बदलेंprintf

echo '1 2 3 4 5 6' | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done

परिणाम

1, 2, 3
4, 5, 6

क्या कोई मुझे बता सकता है कि इन दोनों आदेशों को क्या अलग बनाता है? धन्यवाद ~

जवाबों:


24

यह सिर्फ इको बनाम प्रिंटफ नहीं है

पहले, आइए समझते हैं कि 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 के वर्णों का उपयोग शब्दों को लाइन में विभाजित करने के लिए किया जाता है, उसी नियमों का उपयोग करके जो शेल विस्तार के लिए उपयोग करता है (वर्ड स्प्लिटिंग के तहत ऊपर वर्णित है)।


2
straceमामले और दूसरे के बीच एक उल्लेखनीय अंतर - strace printfउपयोग कर रहा है /usr/bin/printf, जबकि printfसीधे बैश में उसी नाम से शेल बिलिन का उपयोग कर रहा है। वे हमेशा समान नहीं होंगे - उदाहरण के लिए, बैश उदाहरण में प्रारूप विनिर्देशक हैं %qऔर नए संस्करणों में, $()Tसमय प्रारूपण के लिए।
चार्ल्स डफी

7

यह केवल एक सुझाव है और इसका मतलब सर्गेई के जवाब को बिल्कुल भी बदलना नहीं है। मुझे लगता है कि सर्जी ने एक शानदार उत्तर लिखा कि वे मुद्रण में भिन्न क्यों हैं। कैसे पढ़ने पर चर में शेष के साथ सौंप दिया जाता है $cके रूप में चर 3 4 5 6के बाद 1और 2करने के लिए आवंटित कर रहे हैं aऔर bechoआपके लिए वैरिएबल को अलग नहीं करेगा जहां s के printfसाथ होगा %d

हालाँकि, आप उन्हें मूल रूप से कमांड के आरंभ में संख्याओं की प्रतिध्वनित करके आपको समान उत्तर दे सकते हैं:

में /bin/bashआप का उपयोग कर सकते हैं:

echo -e "1 2 3 \n4 5 6"

में /bin/shआप सिर्फ उपयोग कर सकते हैं:

echo "1 2 3 \n4 5 6"

बैश-the-the का उपयोग करता है ताकि वह पहले से ही सक्षम हो सके, जहां sh को इसकी आवश्यकता नहीं है। \nयह एक नई लाइन बनाने का कारण बनता है, इसलिए अब प्रतिध्वनि रेखा को दो अलग-अलग रेखाओं में विभाजित किया जाता है, जिसे अब आपके इको लूप स्टेटमेंट के लिए दो बार उपयोग किया जा सकता है:

:~$ echo -e "1 2 3 \n4 5 6" | while read a b c; do echo "$a, $b, $c"; done
1, 2, 3
4, 5, 6

जो बदले में printfकमांड का उपयोग करने के साथ एक ही आउटपुट का उत्पादन करता है :

:~$ echo -e "1 2 3 \n4 5 6" | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done
1, 2, 3 
4, 5, 6 

में sh

$ echo "1 2 3 \n4 5 6" | while read a b c; do echo "$a, $b, $c"; done
1, 2, 3
4, 5, 6
$ echo "1 2 3 \n4 5 6" | while read a b c ;do printf "%d, %d, %d \n" $a $b $c; done
1, 2, 3 
4, 5, 6 

उम्मीद है की यह मदद करेगा!


echo -ePOSIX के लिए केवल एक एक्सटेंशन नहीं है - लेकिन कोई भी echoजो -eइसके आउटपुट पर उत्सर्जन नहीं करता है, यह देखते हुए कि इनपुट वास्तव में ब्लैक-लेटर विनिर्देशन को टाल / तोड़ रहा है। (बैश डिफ़ॉल्ट रूप से गैर-योग्य होता है, लेकिन मानक के साथ अनुपालन होता है जब दोनों posixऔर xpg_echoझंडे सेट होते हैं; उत्तरार्द्ध को संकलन-समय पर डिफ़ॉल्ट बनाया जा सकता है, जिसका अर्थ है कि बैश के सभी संस्करण echo -eआपके अपेक्षा के अनुरूप काम नहीं करेंगे)।
चार्ल्स डफी

POSIX कल्पना के लिए pubs.opengroup.org/onlinepubs/9699919799/ut उपयोगिता / echo.htmlecho पर APPLICATION USAGE और राष्ट्रीय अनुभाग देखें , स्पष्ट रूप से वर्णन करते हुए कि इनपुट में कहीं भी या बैकस्लैश echoकी उपस्थिति में व्यवहार अप्रभावित है-n , और सलाह दे रहा है कि printfइसके बजाय इस्तेमाल किया जाना चाहिए।
चार्ल्स डफी

@CharlesDuffy क्या मैंने कभी भी खदान में लिखा है कि मुझे उम्मीद है कि यह सभी संस्करणों में काम करेगा? और आपको मेरे उत्तर से क्या समस्या है? यदि आपको लगता है कि आपके पास एक बेहतर समाधान है, तो इसे लोगों को गलत साबित करने की कोशिश करने के बजाय एक उत्तर के रूप में लिखें। मैं आपके जैसे लोगों के साथ इस तरह से कई बार गया हूँ, तो कृपया इस तरह की टिप्पणियों के साथ रुकें। बस मुझे यही कहना है।
टेरेंस

3
@CharlesDuffy Ubuntu में पूछें हम कभी-कभी ऐसे उत्तर लिखते हैं जो अन्य लिनक्स डिस्ट्रोस के लिए गैर-परिवहन योग्य हैं। जैसा कि आपका प्रतिनिधि यहाँ केवल 103 अंक है, आपने उस पर ध्यान नहीं दिया होगा। स्टैक ओवरफ्लो पर आपका प्रतिनिधि हालांकि 123.3K अंक है, इसलिए आप स्पष्ट रूप से सामान्‍य रूप से * NIX सिस्टम के बारे में एक टन सामान जानते हैं। मुझे लगता है कि आप, टेरेस और सर्ग सभी सही हैं, लेकिन विभिन्न कोणों से धागे को देख रहे हैं। मैं आपके लिए एक उत्तर लिखने के लिए भावना (जिसका कोई उद्देश्य नहीं है) प्रतिध्वनि है जो * NIX पोर्टेबल है, या कम से कम लिनक्स डिस्ट्रोस में पोर्टेबल है।
विनयुनुच्स 2 यूनिक्स

2
@CharlesDuffy जबकि मैं आपकी भावना से सहमत हूं कि उत्तर को पोर्टेबल बनाया जाना चाहिए , यह सब उबंटू-उन्मुख साइट के बाद है, इसलिए उबंटू-विशिष्ट उपकरणों को उपयोगकर्ताओं द्वारा ग्रहण किया जाना चाहिए। तो चेतावनी कुछ निहित है। चलिए ज्यादा लंबी चर्चा में नहीं जाते हैं। आप सही हैं कि अन्य गोले पर विचार किया जाना चाहिए, और उत्तरों से सीखने के लिए पढ़े जाने वाले नवाबों पर भी विचार किया जाना चाहिए। एक संक्षिप्त टिप्पणी शायद बात बनाने के लिए पर्याप्त होगी।
सर्गी कोलोडियाज़नी
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.