वे इंटरलेव करते हैं! आपने केवल लघु आउटपुट फटने की कोशिश की, जो कि अनिश्चित रहता है, लेकिन व्यवहार में यह गारंटी देना कठिन है कि कोई विशेष आउटपुट अप्राप्त रहता है।
आउटपुट बफरिंग
यह निर्भर करता है कि प्रोग्राम अपने आउटपुट को कैसे बफर करते हैं। Stdio पुस्तकालय है कि ज्यादातर कार्यक्रमों का उपयोग जब वे लेखन का उपयोग करता है बफ़र्स उत्पादन अधिक कुशल बनाने के। डेटा को आउटपुट करने के बजाय जैसे ही प्रोग्राम किसी फ़ाइल को लिखने के लिए लाइब्रेरी फ़ंक्शन को कॉल करता है, फ़ंक्शन इस डेटा को एक बफर में संग्रहीत करता है, और केवल एक बार बफर भर जाने पर डेटा को आउटपुट करता है। इसका मतलब है कि आउटपुट बैचों में किया जाता है। अधिक सटीक रूप से, तीन आउटपुट मोड हैं:
- बिना बफ़र किए: बिना बफर का उपयोग किए, डेटा तुरंत लिखा जाता है। यह धीमा हो सकता है यदि प्रोग्राम छोटे आउटपुट में अपना आउटपुट लिखता है, जैसे चरित्र द्वारा चरित्र। यह मानक त्रुटि के लिए डिफ़ॉल्ट मोड है।
- पूरी तरह से बफ़र्ड: डेटा केवल तब लिखा जाता है जब बफर भरा हुआ हो। यह एक डिफरेंट मोड है, जिसमें स्टेपर को छोड़कर, किसी पाइप या रेगुलर फाइल पर लिखते हैं।
- लाइन-बफ़र्ड: डेटा प्रत्येक न्यूलाइन के बाद, या जब बफर भरा हुआ हो, तब लिखा जाता है। यह टर्मिनल के लिए लिखने के दौरान डिफ़ॉल्ट मोड है, स्टैडर के अलावा।
प्रोग्राम प्रत्येक फ़ाइल को अलग तरीके से व्यवहार करने के लिए फटकार लगा सकते हैं, और बफर को स्पष्ट रूप से फ्लश कर सकते हैं। जब प्रोग्राम फ़ाइल बंद कर देता है या सामान्य रूप से बाहर निकल जाता है तो बफर अपने आप फ्लश हो जाता है।
यदि सभी प्रोग्राम जो एक ही पाइप पर लिख रहे हैं, या तो लाइन-बफ़र्ड मोड का उपयोग करते हैं, या अनबर्डर मोड का उपयोग करते हैं और आउटपुट फ़ंक्शन के लिए एक कॉल के साथ प्रत्येक पंक्ति लिखते हैं, और यदि लाइनें एक ही चंक में लिखने के लिए पर्याप्त छोटी हैं, तो उत्पादन पूरी लाइनों की एक interleaving होगा। लेकिन यदि कोई प्रोग्राम पूरी तरह से बफर मोड का उपयोग करता है, या यदि लाइनें बहुत लंबी हैं, तो आप मिश्रित लाइनें देखेंगे।
यहां एक उदाहरण है जहां मैं दो कार्यक्रमों से आउटपुट को इंटरलेव करता हूं। मैंने लिनक्स पर GNU कोरुटिल्स का उपयोग किया; इन उपयोगिताओं के विभिन्न संस्करण अलग-अलग व्यवहार कर सकते हैं।
yes aaaa
aaaa
हमेशा के लिए लिखता है जो अनिवार्य रूप से लाइन-बफ़र मोड के बराबर है। yes
उपयोगिता वास्तव में एक समय में कई पंक्तियों लिखते हैं, लेकिन हर बार यह उत्पादन का उत्सर्जन करता है, उत्पादन लाइनों की एक पूर्ण संख्या है।
echo bbbb; done | grep b
bbbb
पूरी तरह से बफर मोड में हमेशा के लिए लिखता है। यह 8192 के बफर आकार का उपयोग करता है, और प्रत्येक पंक्ति 5 बाइट्स लंबी है। चूंकि 5 8192 को विभाजित नहीं करता है, इसलिए लिखने के बीच की सीमाएं सामान्य रूप से एक सीमा रेखा पर नहीं होती हैं।
चलो उन्हें एक साथ पिच करते हैं।
$ { yes aaaa & while true; do echo bbbb; done | grep b & } | head -n 999999 | grep -e ab -e ba
bbaaaa
bbbbaaaa
baaaa
bbbaaaa
bbaaaa
bbbaaaa
ab
bbbbaaa
जैसा कि आप देख सकते हैं, हाँ कभी-कभी बाधित grep और इसके विपरीत। केवल 0.001% लाइनें बाधित हुईं, लेकिन ऐसा हुआ। आउटपुट को यादृच्छिक किया जाता है, इसलिए रुकावटों की संख्या अलग-अलग होगी, लेकिन मैंने हर बार कम से कम कुछ व्यवधानों को देखा। यदि लाइनें लंबी थीं, तो बाधित लाइनों का एक बड़ा हिस्सा होगा, क्योंकि एक बाधा की संभावना बढ़ जाती है क्योंकि प्रति बफर लाइनों की संख्या कम हो जाती है।
आउटपुट बफ़रिंग को समायोजित करने के कई तरीके हैं । मुख्य हैं:
stdbuf -o0
GNU Coreutils और FreeBSD जैसे कुछ अन्य सिस्टमों में पाए गए प्रोग्राम के साथ अपनी डिफ़ॉल्ट सेटिंग्स को बदले बिना उन प्रोग्रामों में बफ़रिंग का उपयोग करें जो stdio लाइब्रेरी को बंद करते हैं । आप वैकल्पिक रूप से लाइन बफरिंग के साथ स्विच कर सकते हैं stdbuf -oL
।
- बस इस उद्देश्य के लिए बनाए गए टर्मिनल के माध्यम से प्रोग्राम के आउटपुट को निर्देशित करके लाइन बफरिंग पर स्विच करें
unbuffer
। कुछ कार्यक्रम अन्य तरीकों से अलग तरह से व्यवहार कर सकते हैं, उदाहरण के लिए grep
डिफ़ॉल्ट रूप से रंगों का उपयोग करता है यदि इसका आउटपुट एक टर्मिनल है।
- उदाहरण के लिए,
--line-buffered
GNU grep पास करके प्रोग्राम को कॉन्फ़िगर करें ।
चलो फिर से ऊपर स्निपेट देखें, इस बार दोनों तरफ लाइन बफरिंग के साथ।
{ stdbuf -oL yes aaaa & while true; do echo bbbb; done | grep --line-buffered b & } | head -n 999999 | grep -e ab -e ba
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
abbbb
तो इस बार हाँ कभी बाधित नहीं किया, लेकिन कभी कभी हाँ बाधित। मैं बाद में क्यों आता हूँ।
पाइप इंटरलेइंग
जब तक प्रत्येक प्रोग्राम एक समय में एक लाइन आउटपुट करता है, और लाइनें काफी कम होती हैं, आउटपुट लाइनें बड़े करीने से अलग हो जाएंगी। लेकिन इसकी एक सीमा है कि इसके काम करने के लिए लाइनें कितनी लंबी हो सकती हैं। पाइप में स्वयं एक स्थानांतरण बफर है। जब कोई प्रोग्राम पाइप से आउटपुट करता है, तो डेटा को लेखक प्रोग्राम से पाइप के ट्रांसफर बफर में कॉपी किया जाता है, और फिर बाद में पाइप के ट्रांसफर बफर से रीडर प्रोग्राम में भेज दिया जाता है। (कम से कम वैचारिक रूप से - कर्नेल कभी-कभी इसे एकल प्रति में अनुकूलित कर सकता है।)
यदि पाइप के ट्रांसफर बफर में फिट होने की तुलना में कॉपी करने के लिए अधिक डेटा है, तो कर्नेल एक बार में एक बफर कॉपी करता है। यदि कई प्रोग्राम एक ही पाइप से लिख रहे हैं, और पहला प्रोग्राम जो कर्नेल चुनता है, वह एक से अधिक बफर लिखना चाहता है, तो इस बात की कोई गारंटी नहीं है कि कर्नेल दूसरी बार फिर से उसी प्रोग्राम को चुनेगा। उदाहरण के लिए, यदि P बफर आकार है, foo
2 * P बाइट्स bar
लिखना चाहता है और 3 बाइट्स लिखना चाहता है, तो एक संभावित इंटरलेविंग P बाइट्स है foo
, फिर 3 बाइट्स bar
, और P बाइट्स से foo
।
ऊपर हाँ + grep उदाहरण के लिए वापस आ रहा है, मेरे सिस्टम पर, yes aaaa
एक ही बार में 8192-बाइट बफर में फिट होने वाली कई पंक्तियों को लिखने के लिए होता है। चूंकि लिखने के लिए 5 बाइट्स हैं (4 प्रिंट करने योग्य अक्षर और नई पंक्ति), इसका मतलब है कि यह हर बार 8190 बाइट्स लिखता है। पाइप बफर का आकार 4096 बाइट्स है। इसलिए हाँ से 4096 बाइट्स प्राप्त करना संभव है, फिर क्रेप से कुछ आउटपुट, और फिर हां (8190 - 4096 = 4094 बाइट्स) से बाकी लेखन। 4096 बाइट्स 819 लाइनों के साथ aaaa
और एक लोन के लिए जगह छोड़ती है a
। इसलिए इस लाइन के साथ एक लाइन a
grep से लिखी जाती है, जिसके साथ एक लाइन दी जाती है abbbb
।
यदि आप क्या चल रहा है, इसका विवरण देखना चाहते हैं, तो getconf PIPE_BUF .
आपको अपने सिस्टम पर पाइप बफर आकार बताएगा, और आप प्रत्येक प्रोग्राम द्वारा किए गए सिस्टम कॉल की पूरी सूची देख सकते हैं
strace -s9999 -f -o line_buffered.strace sh -c '{ stdbuf -oL yes aaaa & while true; do echo bbbb; done | grep --line-buffered b & }' | head -n 999999 | grep -e ab -e ba
स्वच्छ रेखा इंटरलायिंग की गारंटी कैसे दें
यदि लाइन की लंबाई पाइप बफर आकार से छोटी है, तो लाइन बफरिंग गारंटी देती है कि आउटपुट में कोई मिश्रित लाइन नहीं होगी।
यदि लाइन की लंबाई बड़ी हो सकती है, तो एक ही पाइप पर कई प्रोग्राम लिखने पर मनमाने मिश्रण से बचने का कोई तरीका नहीं है। जुदाई सुनिश्चित करने के लिए, आपको प्रत्येक प्रोग्राम को एक अलग पाइप पर लिखने की जरूरत है, और लाइनों को संयोजित करने के लिए एक प्रोग्राम का उपयोग करें। उदाहरण के लिए जीएनयू समानांतर डिफ़ॉल्ट रूप से ऐसा करता है।