पाइप में बफरिंग बंद करें


395

मेरे पास एक स्क्रिप्ट है जो दो कमांड को कॉल करती है:

long_running_command | print_progress

long_running_commandप्रिंट एक प्रगति लेकिन मैं इसे से संतुष्ट नहीं हूं। मैं print_progressइसे और अधिक अच्छा बनाने के लिए उपयोग कर रहा हूं (अर्थात्, मैं एक पंक्ति में प्रगति को प्रिंट करता हूं)।

समस्या: कनेक्शन को एक स्टडआउट के लिए एक पाइप भी एक 4K बफर को सक्रिय करता है, अच्छा प्रिंट प्रोग्राम को कुछ भी नहीं मिलता है ... कुछ भी नहीं ... कुछ भी नहीं ... पूरी तरह से ... :)

मैं long_running_command(नहीं, मेरे पास स्रोत नहीं है) के लिए मैं 4K बफर को कैसे अक्षम कर सकता हूं ?


1
इसलिए जब आप पाइपिंग के बिना long_running_command चलाते हैं तो आप प्रगति के अपडेट को ठीक से देख सकते हैं, लेकिन पाइपिंग करते समय वे बफ़र्ड हो जाते हैं?

1
हां, बिल्कुल ऐसा ही होता है।
आरोन दिगुल्ला

20
बफरिंग को नियंत्रित करने के एक सरल तरीके के लिए असमर्थता दशकों से एक समस्या रही है। उदाहरण के लिए, देखें: marc.info/?l=glibc-bug&m=98313957306297&w=4 जो मूल रूप से कहता है "मुझे ऐसा करने के लिए नहीं उकसाया जा सकता है और यहां मेरी स्थिति को सही ठहराने के लिए कुछ ताली- फांद है "


1
यह वास्तव में stdio नहीं पाइप है जो पर्याप्त डेटा की प्रतीक्षा करते समय देरी का कारण बनता है। पाइप की क्षमता होती है, लेकिन जैसे ही पाइप पर कोई डेटा लिखा जाता है, वह तुरंत दूसरे छोर पर पढ़ने के लिए तैयार होता है।
सैम वाटकिंस

जवाबों:


254

आप unbufferकमांड का उपयोग कर सकते हैं (जो expectपैकेज के हिस्से के रूप में आता है ), उदाहरण के लिए

unbuffer long_running_command | print_progress

unbufferlong_running_commandएक pseudoterminal (pty) के माध्यम से जुड़ता है , जो सिस्टम को एक इंटरैक्टिव प्रक्रिया के रूप में मानता है, इसलिए पाइपलाइन में 4-kiB बफरिंग का उपयोग नहीं करना जो देरी का संभावित कारण है।

लंबी पाइपलाइनों के लिए, आपको प्रत्येक कमांड (अंतिम एक को छोड़कर) को अनफ़र करना पड़ सकता है, जैसे

unbuffer x | unbuffer -p y | z

3
वास्तव में, इंटरैक्टिव प्रक्रियाओं से जुड़ने के लिए एक पीटीआई का उपयोग सामान्य रूप से उम्मीद का सच है।

15
जब पाइप लाइनिंग unbuffer पर कॉल करता है, तो आपको -p तर्क का उपयोग करना चाहिए ताकि unbuffer स्टड से पढ़े।

26
नोट: डेबियन सिस्टम पर, कहा जाता है expect_unbufferऔर में है expect-dev, नहीं पैकेज expectपैकेज
bdonlan

4
@bdonlan: कम से कम उबंटू (डेबियन-आधारित) पर, expect-devदोनों प्रदान करता है unbufferऔर expect_unbuffer(पूर्व में उत्तरार्द्ध का सहिष्णु है)। expect 5.44.1.14-1(2009) के बाद से लिंक उपलब्ध हैं ।
13

1
नोट: उबंटू 14.04.x ​​सिस्टम पर, यह उम्मीद-देव पैकेज में भी है।
अलेक्जेंड्रे माजेल

462

इस बिल्ली को त्वचा देने का एक और तरीका stdbufप्रोग्राम का उपयोग करना है, जो ग्नू कोरुटिल्स का हिस्सा है (फ्रीबीएसडी का भी अपना एक है)।

stdbuf -i0 -o0 -e0 command

यह इनपुट, आउटपुट और त्रुटि के लिए पूरी तरह से बफरिंग को बंद कर देता है। कुछ अनुप्रयोगों के लिए, प्रदर्शन कारणों से लाइन बफरिंग अधिक उपयुक्त हो सकती है:

stdbuf -oL -eL command

ध्यान दें कि यह केवल गतिशील रूप से जुड़े अनुप्रयोगों के लिए stdioबफ़रिंग ( printf(), fputs()...) के लिए काम करता है , और केवल अगर यह अनुप्रयोग अन्यथा अपने मानक धाराओं के बफ़रिंग को अपने आप से समायोजित नहीं करता है, हालांकि यह कि अधिकांश अनुप्रयोगों को कवर करना चाहिए।


6
"अनबफ़र" को उबंटू में स्थापित करने की आवश्यकता है, जो पैकेज के अंदर है: उम्मीद-देव जो 2 एमबी है ...
लेप

2
यह डिफॉल्ट रास्पियन इंस्टाल को अनबफर लॉगिंग करने पर बहुत अच्छा काम करता है। मुझे sudo stdbuff … commandकाम मिला, हालाँकि stdbuff … sudo commandनहीं।
natevw

20
@qdii के stdbufसाथ काम नहीं करता है tee, क्योंकि teeद्वारा निर्धारित चूक को ओवरराइट करता है stdbuf। का मैनुअल पेज देखें stdbuf
ceving

5
@lepe विचित्र रूप से, unbuffer पर x11 और tcl / tk पर निर्भरता है, जिसका अर्थ है कि वास्तव में इसे जरूरत है> 80 एमबी यदि आप इसे बिना सर्वर पर स्थापित कर रहे हैं।
जपतोकल

10
@qdii अपनी गतिशील रूप से भरी हुई लाइब्रेरी को सम्मिलित करने के लिए तंत्र का stdbufउपयोग करता LD_PRELOADहै libstdbuf.so। इसका मतलब यह है कि यह इन प्रकार के निष्पादनयोग्य के साथ काम नहीं करेगा: मानक लिबास का उपयोग न करके, सेट्युइड या फ़ाइल क्षमताओं के साथ सेट किया गया। इन मामलों में unbuffer/ script/ के साथ समाधान का उपयोग करना बेहतर है socatसेतुबंध / क्षमताओं के साथ stdbuf भी देखें ।
पाबौक

75

फिर भी एक अन्य तरीका है कि आप एक छद्म टर्मिनल (pty) में चलने वाली कमांड long_running_commandका उपयोग करें ।scriptlong_running_command

script -q /dev/null long_running_command | print_progress      # FreeBSD, Mac OS X
script -c "long_running_command" /dev/null | print_progress    # Linux

15
+1 अच्छी चाल, चूंकि scriptइतनी पुरानी कमांड है, यह सभी यूनिक्स जैसे प्लेटफार्मों पर उपलब्ध होनी चाहिए।
आरोन दिगुल्ला

5
आपको -qलिनक्स पर भी ज़रूरत है:script -q -c 'long_running_command' /dev/null | print_progress
12

1
ऐसा लगता है कि स्क्रिप्ट से पढ़ता है stdin, जो long_running_commandपृष्ठभूमि में इस तरह के रन को असंभव बना देता है , कम से कम जब इंटरैक्टिव टर्मिनल से शुरू होता है। वर्कअराउंड करने के लिए, मैं स्टड पुनर्निर्देशित करने में सक्षम था /dev/null, क्योंकि मेरा long_running_commandउपयोग नहीं करता है stdin
हरदिवस

1
यहां तक ​​कि एंड्रॉइड पर भी काम करता है।
२:३०

3
एक महत्वपूर्ण नुकसान: ctrl-z अब काम नहीं करता (यानी मैं स्क्रिप्ट को निलंबित नहीं कर सकता)। यह द्वारा तय किया जा सकता है, उदाहरण के लिए: गूंज | sudo script -c / usr / local / bin / ec2-snapshot-all / dev / null | ts, अगर आपको मन नहीं है कार्यक्रम के साथ बातचीत करने में सक्षम होने में।
rlpowell

66

के लिए grep, sedऔर awkआप आउटपुट को लाइन बफ़र्ड करने के लिए बाध्य कर सकते हैं। आप उपयोग कर सकते हैं:

grep --line-buffered

बल उत्पादन लाइन बफ़र किया जा करने के लिए। डिफ़ॉल्ट रूप से, आउटपुट लाइन बफ़र किया जाता है जब मानक आउटपुट एक टर्मिनल और ब्लॉक-बफ़र अन्य-वार होता है।

sed -u

आउटपुट लाइन बफ़र करें।

अधिक जानकारी के लिए यह पेज देखें: http://www.perkin.org.uk/posts/how-to-fix-stdio-buffin.html


51

अगर यह समस्या है कि जब कोई टर्मिनल पर नहीं जाता है, तो इसकी बफरिंग / फ्लशिंग को लिबास में बदलना चाहिए, तो आपको सोसैट की कोशिश करनी चाहिए । आप लगभग किसी भी प्रकार के I / O तंत्र के बीच एक द्विदिश धारा बना सकते हैं। उनमें से एक एक छद्म ट्टी के लिए बोलने वाला एक फोर्कड प्रोग्राम है।

 socat EXEC:long_running_command,pty,ctty STDIO 

यह क्या करता है

  • एक छद्म tty बनाएँ
  • कांटा long_running_command stty / stdout के रूप में pty के दास पक्ष के साथ
  • पीटीआई के मास्टर पक्ष और दूसरे पते के बीच एक द्विदिश धारा स्थापित करें (यहां यह STDIO है)

यदि यह आपको वही आउटपुट देता है long_running_command, तो आप पाइप से जारी रख सकते हैं।

संपादित करें: वाह जवाब नहीं देखा है! खैर, समाज वैसे भी एक महान उपकरण है, इसलिए मैं बस इस जवाब को छोड़ सकता हूं


1
... और मैं समाज के बारे में नहीं जानता था - नेटकाट की तरह थोड़े ही दिखता है शायद इतना ही। ;) धन्यवाद और +1।

3
मैं socat -u exec:long_running_command,pty,end-close -यहाँ उपयोग करता हूँ
स्टीफन चेज़ेलस

20

आप उपयोग कर सकते हैं

long_running_command 1>&2 |& print_progress

समस्या यह है कि libc लाइन-बफर करेगा जब स्क्रीन पर stdout, और पूर्ण-बफर जब किसी फ़ाइल पर stdout होता है। लेकिन स्टाडर के लिए नो-बफर।

मुझे नहीं लगता कि यह पाइप बफर की समस्या है, यह सब libc की बफर पॉलिसी के बारे में है।


आप सही हे; मेरा सवाल अभी भी है: मैं recompiling के बिना libc की बफर नीति को कैसे प्रभावित कर सकता हूं?
एरोन दिगुल्ला

@ स्टीफनचैलेजेलस fd1 को stderr पर पुनर्निर्देशित किया जाएगा
वांग हांगकिन

@ स्टीफनचेज़ेलैस मैं अपने बहस बिंदु नहीं मिलता। plz एक परीक्षण करते हैं, यह काम करता है
वांग हांगकिन

3
ठीक है, यह क्या हो रहा है कि दोनों zsh(जहां |&csh से अनुकूलित किया गया है) और bash, जब आप करते हैं cmd1 >&2 |& cmd2, तो fd 1 और 2 दोनों बाहरी स्टडआउट से जुड़े होते हैं। तो यह बफरिंग को रोकने पर काम करता है जब बाहरी स्टडआउट एक टर्मिनल होता है, लेकिन केवल इसलिए कि आउटपुट पाइप के माध्यम से नहीं जाता है (इसलिए print_progressकुछ भी प्रिंट नहीं करता है)। इसलिए यह वैसा ही है long_running_command & print_progress(सिवाय इसके कि Print_progress स्टड एक पाइप है जिसमें कोई लेखक नहीं है)। ls -l /proc/self/fd >&2 |& catकी तुलना में आप सत्यापित कर सकते हैं ls -l /proc/self/fd |& cat
स्टीफन चेजलस

3
ऐसा इसलिए |&है क्योंकि 2>&1 |शाब्दिक रूप से यह छोटा है । तो cmd1 |& cmd2है cmd1 1>&2 2>&1 | cmd2। तो, fd 1 और 2 दोनों मूल स्टडर से जुड़े हुए हैं, और पाइप के लिए कुछ भी नहीं बचा है। ( s/outer stdout/outer stderr/gमेरी पिछली टिप्पणी में)।
स्टीफन चेजलस

11

यह मामला हुआ करता था, और शायद अभी भी मामला है, कि जब मानक आउटपुट को टर्मिनल पर लिखा जाता है, तो यह डिफ़ॉल्ट रूप से लाइन बफ़र किया जाता है - जब एक नई लाइन लिखी जाती है, तो लाइन टर्मिनल को लिखी जाती है। जब मानक आउटपुट एक पाइप को भेजा जाता है, तो यह पूरी तरह से बफर हो जाता है - इसलिए डेटा केवल पाइपलाइन में अगली प्रक्रिया के लिए भेजा जाता है जब मानक I / O बफर भर जाता है।

यही परेशानी का स्रोत है। मुझे यकीन नहीं है कि क्या आप पाइप में प्रोग्राम लेखन को संशोधित किए बिना इसे ठीक करने के लिए बहुत कुछ कर सकते हैं। आप setvbuf()फंक्शन का उपयोग _IOLBFध्वज के साथ बिना शर्त stdoutबफर मोड में करने के लिए कर सकते हैं। लेकिन मुझे लगता है कि एक कार्यक्रम पर लागू करने के लिए एक आसान तरीका नहीं है। या कार्यक्रम fflush()उपयुक्त बिंदुओं (आउटपुट की प्रत्येक पंक्ति के बाद) पर कर सकता है, लेकिन एक ही टिप्पणी लागू होती है।

मुझे लगता है कि यदि आपने पाइप को एक छद्म टर्मिनल के साथ बदल दिया है, तो मानक I / O लाइब्रेरी को लगता है कि आउटपुट एक टर्मिनल था (क्योंकि यह टर्मिनल का एक प्रकार है) और स्वचालित रूप से बफर को लाइन करेगा। हालांकि, चीजों से निपटने का एक जटिल तरीका है।


7

मुझे पता है कि यह एक पुराना सवाल है और पहले से ही बहुत सारे उत्तर थे, लेकिन यदि आप बफर समस्या से बचना चाहते हैं, तो बस कुछ इस तरह की कोशिश करें:

stdbuf -oL tail -f /var/log/messages | tee -a /home/your_user_here/logs.txt

यह वास्तविक समय में लॉग में आउटपुट करेगा और उन्हें logs.txtफ़ाइल में भी सहेजेगा और बफर अब tail -fकमांड को प्रभावित नहीं करेगा ।


4
यह दूसरे उत्तर की तरह दिखता है: - /
हारून डिगुल्ला

2
stdbuf gnu coreutils में शामिल है (मैंने नवीनतम संस्करण 8.25 पर सत्यापित किया है)। यह एक एम्बेडेड लिनक्स पर काम करता है।
झाउरुफ़ेई

Stdbuf के प्रलेखन से, NOTE: If COMMAND adjusts the buffering of its standard streams ('tee' does for example) then that will override corresponding changes by 'stdbuf'.
shrewmouse

6

मुझे नहीं लगता कि समस्या पाइप के साथ है। ऐसा लगता है कि आपकी लंबी चलने वाली प्रक्रिया अपने बफर को अक्सर पर्याप्त रूप से फ्लश नहीं कर रही है। पाइप के बफर आकार को बदलना इसे प्राप्त करने के लिए एक हैक होगा, लेकिन मुझे लगता है कि कर्नेल के पुनर्निर्माण के बिना यह संभव नहीं है - ऐसा कुछ जिसे आप हैक के रूप में नहीं करना चाहते हैं, क्योंकि यह संभवतः एवेर्ली अन्य प्रक्रियाओं को बहुत प्रभावित करता है।


18
मूल कारण यह है कि यदि स्टडआउट ट्टी नहीं है तो libc 4k बफरिंग पर स्विच करता है।
आरोन दिगुल्ला

5
वो बहुत रुचिकर है ! क्योंकि पाइप किसी भी बफरिंग का कारण नहीं है। वे बफरिंग प्रदान करते हैं, लेकिन यदि आप एक पाइप से पढ़ते हैं, तो आपको जो भी डेटा मिलता है, आपको पाइप में बफर के लिए इंतजार नहीं करना पड़ता है। तो अपराधी आवेदन में stdio बफरिंग होगा।

3

यहाँ इस पोस्ट के अनुसार , आप पाइप को एक सिंगल बाइट 512 बाइट को कम करने की कोशिश कर सकते हैं। यह निश्चित रूप से बफरिंग को बंद नहीं करेगा, लेकिन अच्छी तरह से, 512 बाइट्स 4K: 3 से कम है


3

चाड के उत्तर के समान नस में , आप इस तरह से एक छोटी सी स्क्रिप्ट लिख सकते हैं:

# save as ~/bin/scriptee, or so
script -q /dev/null sh -c 'exec cat > /dev/null'

फिर इस scripteeआदेश का उपयोग प्रतिस्थापन के रूप में करें tee

my-long-running-command | scriptee

काश, मैं लिनक्स में पूरी तरह से काम करने के लिए इस तरह का एक संस्करण प्राप्त करने के लिए प्रतीत नहीं हो सकता है, इसलिए बीएसडी-शैली यूनिक्स तक सीमित लगता है।

लिनक्स पर, यह करीब है, लेकिन जब आप इसे पूरा नहीं करते हैं, तब तक आपको अपना संकेत वापस नहीं मिलता है (जब तक कि आप एंटर दबाते हैं, आदि) ...

script -q -c 'cat > /proc/self/fd/1' /dev/null

वह काम क्यों करता है? क्या "स्क्रिप्ट" बफ़रिंग बंद कर देता है?
आरोन दिगुल्ला

@ एरॉन डिगुल्ला: scriptएक टर्मिनल का अनुकरण करता है, इसलिए हां, मेरा मानना ​​है कि यह बफरिंग बंद कर देता है। यह भेजे गए प्रत्येक चरित्र को भी वापस ले जाता है - यही वजह catहै /dev/nullकि उदाहरण में भेजा गया है। जहां तक ​​कार्यक्रम के अंदर चलने scriptका सवाल है, यह एक संवादात्मक सत्र के लिए बात कर रहा है। मेरा मानना ​​है कि यह expectइस संबंध में समान है , लेकिन scriptसंभावना आपके आधार प्रणाली का हिस्सा है।
jwd

मेरे द्वारा उपयोग किए जाने teeका कारण एक फ़ाइल में स्ट्रीम की एक प्रति भेजना है। फ़ाइल को कहां निर्दिष्ट किया जाता है scriptee?
ब्रूनो ब्रोंस्की

@BrunoBronosky: आप सही हैं, यह इस कार्यक्रम के लिए एक बुरा नाम है। यह वास्तव में 'टी' ऑपरेशन नहीं कर रहा है। यह मूल सवाल के अनुसार, आउटपुट के बफरिंग को अक्षम कर रहा है। हो सकता है कि इसे "स्क्रिप्टकैट" कहा जाना चाहिए (हालांकि यह या तो सहमति नहीं कर रहा है ...)। भले ही, आप catकमांड को बदल सकते हैं tee myfile.txt, और आपको वह प्रभाव प्राप्त करना चाहिए जो आप चाहते हैं।
jwd

2

मुझे यह चतुर समाधान मिला: (echo -e "cmd 1\ncmd 2" && cat) | ./shell_executable

यह ट्रिक करता है। catअतिरिक्त इनपुट (ईओएफ तक) पढ़ेगा और echoइनपुट के स्ट्रीम में अपनी दलीलें डालने के बाद पाइप को पास करेगा shell_executable


2
वास्तव में, catके उत्पादन को नहीं देखता है echo; आप सिर्फ एक सबशेल में दो कमांड चलाते हैं और दोनों का आउटपुट पाइप में भेजा जाता है। उपधारा ('बिल्ली') में दूसरी कमांड माता-पिता / बाहरी स्टड से पढ़ती है, इसीलिए यह काम करता है।
आरोन दिगुल्ला

0

के अनुसार इस पाइप बफर आकार कर्नेल में सेट किया जा रहा है और अपने गिरी पुनः संकलित करने के लिए परिवर्तन करने के लिए आप की आवश्यकता होगी।


7
मेरा मानना ​​है कि एक अलग बफर है।
शमूएल एडविन वार्ड
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.