मेमोरी में पढ़ने और दो बार कंप्यूटिंग करने की तुलना में दो बार तेजी से फाइल पर चलना क्यों है?


26

मैं निम्नलिखित तुलना कर रहा हूँ

tail -n 1000000 stdout.log | grep -c '"success": true'
tail -n 1000000 stdout.log | grep -c '"success": false'

निम्नलिखित के साथ

log=$(tail -n 1000000 stdout.log)
echo "$log" | grep -c '"success": true'
echo "$log" | grep -c '"success": false'

और आश्चर्यजनक रूप से दूसरा पहले की तुलना में लगभग 3 गुना अधिक समय लेता है। यह तेज होना चाहिए, है ना?


क्या ऐसा हो सकता है क्योंकि दूसरा समाधान, फ़ाइल सामग्री को 3 बार पढ़ा जाता है, और पहले उदाहरण में केवल दो बार?
लॉरेंट सी।

4
दूसरे उदाहरण में कम से कम, अपने $( command substitution )है नहीं स्ट्रीम किया। बाकी सभी पाइप के माध्यम से समवर्ती रूप से होते हैं, लेकिन दूसरे उदाहरण में आपको पूरा होने के लिए इंतजार करना होगा log=। इसके साथ आज़माएँ << यहाँ \ n $ {लॉग = $ (कमांड)} \ n - ईई देखें - आपको क्या मिलता है।
मिकसेर

अत्यंत बड़ी फ़ाइलों, मेमोरी की कमी वाली मशीनों, या अधिक वस्तुओं के मामले में grep, आप कुछ स्पीडअप का उपयोग कर सकते हैं, teeइसलिए फ़ाइल को केवल एक बार पढ़ा जाना चाहिए। cat stdout.log | tee >/dev/null >(grep -c 'true'>true.cnt) >(grep -c 'false'>false.cnt); cat true.cnt; cat false.cnt
मैट

@LaurentC।, नहीं, यह केवल एक बार दूसरे उदाहरण में पढ़ा जाता है। पूंछ करने के लिए केवल एक कॉल है।
Psusi

अब इनकी तुलना tail -n 10000 | fgrep -c '"success": true'झूठ और झूठ से करें।
कोजिरो

जवाबों:


11

एक तरफ, पहली विधि tailदो बार कॉल करती है , इसलिए उसे दूसरी विधि की तुलना में अधिक काम करना पड़ता है जो केवल एक बार ऐसा करता है। दूसरी ओर, दूसरी विधि में डेटा को शेल में कॉपी करना और फिर वापस बाहर करना है, इसलिए इसे पहले संस्करण की तुलना में अधिक काम करना होगा जहां tailसीधे पाइप किया जाता है grep। मल्टी-प्रोसेसर मशीन पर पहली विधि का अतिरिक्त लाभ है: grepइसके समानांतर काम कर सकते हैं tail, जबकि दूसरी विधि को सख्ती से क्रमबद्ध किया जाता है, पहले tail, फिर grep

तो कोई स्पष्ट कारण नहीं है कि एक दूसरे से तेज क्यों होना चाहिए।

यदि आप यह देखना चाहते हैं कि क्या चल रहा है, तो देखें कि शेल किस सिस्टम को कॉल करता है। विभिन्न गोले के साथ भी प्रयास करें।

strace -t -f -o 1.strace sh -c '
  tail -n 1000000 stdout.log | grep "\"success\": true" | wc -l;
  tail -n 1000000 stdout.log | grep "\"success\": false" | wc -l'

strace -t -f -o 2-bash.strace bash -c '
  log=$(tail -n 1000000 stdout.log);
  echo "$log" | grep "\"success\": true" | wc -l;
  echo "$log" | grep "\"success\": true" | wc -l'

strace -t -f -o 2-zsh.strace zsh -c '
  log=$(tail -n 1000000 stdout.log);
  echo "$log" | grep "\"success\": true" | wc -l;
  echo "$log" | grep "\"success\": true" | wc -l'

विधि 1 के साथ, मुख्य चरण हैं:

  1. tail पढ़ता है और अपना शुरुआती बिंदु खोजना चाहता है।
  2. tail4096-बाइट विखंडू लिखते हैं जो grepउपवास के रूप में पढ़ते हैं वे उत्पादित होते हैं।
  3. दूसरी खोज स्ट्रिंग के लिए पिछले चरण को दोहराएं।

विधि 2 के साथ, मुख्य चरण हैं:

  1. tail पढ़ता है और अपना शुरुआती बिंदु खोजना चाहता है।
  2. tail 4096-बाइट विखंडू लिखते हैं, जो एक बार में 128 बाइट पढ़ता है, और zsh एक समय में 4096 बाइट पढ़ता है।
  3. बैश या zsh 4096-बाइट चंक्स लिखते हैं जो grepजितनी तेजी से पढ़ते हैं उतना ही वे उत्पादित होते हैं।
  4. दूसरी खोज स्ट्रिंग के लिए पिछले चरण को दोहराएं।

बाश के 128-बाइट विखंडू जब कमांड प्रतिस्थापन के आउटपुट को पढ़ते हैं, तो यह काफी धीमा हो जाता है; zsh मेरे लिए विधि 1 के रूप में उपवास के रूप में बाहर आता है। आपका माइलेज CPU प्रकार और संख्या, शेड्यूलर कॉन्फ़िगरेशन, शामिल टूल के संस्करण और डेटा के आकार के आधार पर भिन्न हो सकता है।


क्या 4k आकृति पृष्ठ-आकार पर निर्भर है? मेरा मतलब है, पूंछ और zsh दोनों सिर्फ mmaping syscalls हैं? (संभवतः यह गलत शब्दावली है, हालांकि मुझे उम्मीद नहीं है ...) अलग तरीके से क्या करना है?
मोकेसर

यह गिल्स पर हाजिर है! Zsh के साथ दूसरी विधि मेरी मशीन पर थोड़ी तेज़ है।
फन्नेहे

महान काम गिल्स, टीके।
X Tian

@mikeserv मैंने यह देखने के लिए स्रोत को नहीं देखा कि ये प्रोग्राम आकार कैसे चुनते हैं। 4096 को देखने के लिए सबसे संभावित कारण एक अंतर्निर्मित स्थिरांक या st_blksizeएक पाइप के लिए मूल्य होगा, जो कि इस मशीन पर 4096 है (और मुझे नहीं पता कि क्या ऐसा इसलिए है क्योंकि यह एमएमयू पृष्ठ का आकार है)। बैश का 128 बिल्ट-इन स्थिरांक होगा।
गिल्स एसओ- बुराई को रोकें '

@Gilles, विचारशील उत्तर के लिए धन्यवाद। Ive हाल ही में पृष्ठ आकार के बारे में उत्सुक हैं।
मिकसेर्व

26

मैंने निम्नलिखित परीक्षण किया है और मेरे सिस्टम पर परिणामी अंतर दूसरी स्क्रिप्ट के लिए लगभग 100 गुना लंबा है।

मेरी फाइल स्ट्रेस आउटपुट कहलाती है bigfile

$ wc -l bigfile.log 
1617000 bigfile.log

स्क्रिप्ट

xtian@clafujiu:~/tmp$ cat p1.sh
tail -n 1000000 bigfile.log | grep '"success": true' | wc -l
tail -n 1000000 bigfile.log | grep '"success": false' | wc -l

xtian@clafujiu:~/tmp$ cat p2.sh
log=$(tail -n 1000000 bigfile.log)
echo "$log" | grep '"success": true' | wc -l
echo "$log" | grep '"success": true' | wc -l

मेरे पास वास्तव में grep के लिए कोई मैच नहीं है इसलिए अंतिम पाइप के माध्यम से कुछ भी नहीं लिखा जाता है wc -l

यहाँ समय हैं:

xtian@clafujiu:~/tmp$ time bash p1.sh
0
0

real    0m0.381s
user    0m0.248s
sys 0m0.280s
xtian@clafujiu:~/tmp$ time bash p2.sh
0
0

real    0m46.060s
user    0m43.903s
sys 0m2.176s

इसलिए मैंने स्ट्रेस कमांड के माध्यम से फिर से दो स्क्रिप्टों को चलाया

strace -cfo p1.strace bash p1.sh
strace -cfo p2.strace bash p2.sh

यहाँ निशान से परिणाम हैं:

$ cat p1.strace 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 97.24    0.508109       63514         8         2 waitpid
  1.61    0.008388           0     84569           read
  1.08    0.005659           0     42448           write
  0.06    0.000328           0     21233           _llseek
  0.00    0.000024           0       204       146 stat64
  0.00    0.000017           0       137           fstat64
  0.00    0.000000           0       283       149 open
  0.00    0.000000           0       180         8 close
...
  0.00    0.000000           0       162           mmap2
  0.00    0.000000           0        29           getuid32
  0.00    0.000000           0        29           getgid32
  0.00    0.000000           0        29           geteuid32
  0.00    0.000000           0        29           getegid32
  0.00    0.000000           0         3         1 fcntl64
  0.00    0.000000           0         7           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    0.522525                149618       332 total

और P2.strace

$ cat p2.strace 
% time     seconds  usecs/call     calls    errors syscall
------ ----------- ----------- --------- --------- ----------------
 75.27    1.336886      133689        10         3 waitpid
 13.36    0.237266          11     21231           write
  4.65    0.082527        1115        74           brk
  2.48    0.044000        7333         6           execve
  2.31    0.040998        5857         7           clone
  1.91    0.033965           0    705681           read
  0.02    0.000376           0     10619           _llseek
  0.00    0.000000           0       248       132 open
...
  0.00    0.000000           0       141           mmap2
  0.00    0.000000           0       176       126 stat64
  0.00    0.000000           0       118           fstat64
  0.00    0.000000           0        25           getuid32
  0.00    0.000000           0        25           getgid32
  0.00    0.000000           0        25           geteuid32
  0.00    0.000000           0        25           getegid32
  0.00    0.000000           0         3         1 fcntl64
  0.00    0.000000           0         6           set_thread_area
------ ----------- ----------- --------- --------- ----------------
100.00    1.776018                738827       293 total

विश्लेषण

आश्चर्य की बात नहीं, दोनों मामलों में ज्यादातर समय एक प्रक्रिया को पूरा करने के लिए इंतजार में बिताया जाता है, लेकिन पी 2 की तुलना में 2.63 गुना अधिक समय तक इंतजार करता है, और जैसा कि दूसरों ने उल्लेख किया है, आप पी 2 में देरी से शुरू कर रहे हैं।

तो अब के बारे में भूल जाते हैं waitpid, %स्तंभ पर ध्यान न दें और दोनों निशान पर सेकंड कॉलम को देखो।

सबसे बड़ा समय p1 पढ़ने में अपना अधिकांश समय संभवतः समझ में आता है, क्योंकि पढ़ने के लिए एक बड़ी फ़ाइल है, लेकिन p1 की तुलना में पढ़ने में P2 28.82 गुना अधिक समय खर्च करता है। - bashइतनी बड़ी फ़ाइल को एक चर में पढ़ने की उम्मीद नहीं है और शायद एक समय में बफर पढ़ रहा है, लाइनों में विभाजित हो रहा है और फिर एक और प्राप्त कर रहा है।

पी 1 के लिए रीड काउंट पी 2 705k बनाम 84k है, प्रत्येक को कर्नेल स्पेस में एक संदर्भ स्विच की आवश्यकता होती है और फिर से बाहर पढ़ा जाता है। लगभग 10 बार रीड और संदर्भ स्विच की संख्या।

लिखने में समय p2 41.93 गुना अधिक खर्च करता है p1 से लिखने में

लिखने की गिनती p1, P2, 42k बनाम 21k से अधिक लिखता है, हालांकि वे बहुत तेज हैं।

संभवतः पूंछ लिखने वाले बफ़र्स के विपरीत echoलाइनों के कारण grep

इसके अलावा , P2 पढ़ने में लिखने की तुलना में अधिक समय खर्च करता है, p1 दूसरा तरीका है!

अन्य कारकbrk सिस्टम कॉल की संख्या को देखें : P2 खर्च करता है 2.42 गुना अधिक ब्रेकिंग की तुलना में यह पढ़ता है! P1 में (यह रजिस्टर भी नहीं है)। brkजब प्रोग्राम को अपने पते की जगह का विस्तार करने की आवश्यकता होती है, क्योंकि शुरू में पर्याप्त आवंटित नहीं किया गया था, तो यह संभवतः उस फ़ाइल को चर में पढ़ने के लिए बैश होने के कारण है, और यह उम्मीद नहीं है कि यह बड़ा हो, और जैसा @ @aiai ने उल्लेख किया है, अगर फ़ाइल बहुत बड़ी है, यहां तक ​​कि काम नहीं करेगा।

tailशायद काफी कुशल फ़ाइल रीडर है, क्योंकि यह वही है जो इसे करने के लिए डिज़ाइन किया गया था, यह संभवतः फ़ाइल को स्कैन करता है और लाइन ब्रेक के लिए स्कैन करता है, इस प्रकार कर्नेल को i / o को अनुकूलित करने की अनुमति देता है। बैश पढ़ना और लिखना दोनों समय बिताना उतना अच्छा नहीं है।

p2 44ms और में 41ms खर्च करता है cloneऔर execvयह p1 के लिए एक औसत दर्जे का राशि नहीं है। संभवतः पूंछ से चर को पढ़ना और बनाना।

अंत में टोटल्स p1 ~ 150k सिस्टम कॉल बनाम P2 740k (4.93 गुना अधिक) निष्पादित करता है।

वेटपिड को खत्म करते हुए, पी 1 0.014416 सेकंड सिस्टम कॉलिंग, पी 2 0.439132 सेकंड (30 गुना लंबा) खर्च करता है।

ऐसा प्रतीत होता है कि P2 उपयोगकर्ता के स्पेस में अधिकतर समय बिताता है, सिवाय सिस्टम कॉल्स के पूरा होने के इंतजार के और कुछ भी नहीं करने के लिए और मेमोरी को फिर से व्यवस्थित करने के लिए, p1 अधिक लिखता है, लेकिन अधिक कुशल है और सिस्टम के कम लोड का कारण बनता है, और इसलिए तेजी से होता है।

निष्कर्ष

मैं बैश स्क्रिप्ट लिखते समय मेमोरी के माध्यम से कोडिंग के बारे में चिंता करने की कोशिश नहीं करूंगा, इसका मतलब यह नहीं है कि आप कुशल होने की कोशिश नहीं करते हैं।

tailयह ऐसा करने के लिए डिज़ाइन किया गया है, यह संभवतः memory mapsफ़ाइल है ताकि यह पढ़ने में कुशल हो और कर्नेल को i / o को ऑप्टिमाइज़ करने की अनुमति दे।

अपनी समस्या को अनुकूलित करने का एक बेहतर तरीका हो सकता है कि आप grep"" सफलता ": लाइनों और फिर ट्रस और फाल्स की गिनती करें, grepएक गिनती विकल्प है जो फिर से बचा जाता है wc -l, या इससे भी बेहतर अभी भी, पूंछ के माध्यम से पूंछ awkऔर गिनती की गणना करें" समवर्ती रूप से झूठ बोलता है। P2 न केवल लंबे समय लेता है, बल्कि सिस्टम में लोड जोड़ता है, जबकि मेमोरी को ब्रक्स के साथ फेरबदल किया जा रहा है।


2
टीएल; डीआर: मॉलोक (); अगर आप $ लॉग को बता सकते हैं कि यह कितना बड़ा होना चाहिए और इसे बिना किसी reallocations के एक ऑप में जल्दी से लिख सकता है, तो यह तेजी से होने की संभावना है।
क्रिस के

5

वास्तव में पहला समाधान फ़ाइल को मेमोरी में भी पढ़ता है! इसे कैशिंग कहा जाता है और स्वचालित रूप से ऑपरेटिंग सिस्टम द्वारा किया जाता है।

और जैसा कि पहले से ही सही ढंग से से समझाया mikeserv पहला समाधान exectutes grep जबकि फ़ाइल पढ़ा जा रहा है दूसरा समाधान कार्यान्वित जबकि यह बाद फ़ाइल से पढ़ा जा चुका है tail

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


3

मुझे लगता है कि मुख्य अंतर बहुत echoधीमा है। इस पर विचार करो:

$ time (tail -n 1000000 foo | grep 'true' | wc -l; 
        tail -n 1000000 foo | grep 'false' | wc -l;)
666666
333333

real    0m0.999s
user    0m1.056s
sys     0m0.136s

$ time (log=$(tail -n 1000000 foo); echo "$log" | grep 'true' | wc -l; 
                                    echo "$log" | grep 'false' | wc -l)
666666
333333

real    0m4.132s
user    0m3.876s
sys     0m0.468s

$ time (tail -n 1000000 foo > bb;  grep 'true' bb | wc -l; 
                                   grep 'false' bb | wc -l)
666666
333333

real    0m0.568s
user    0m0.512s
sys     0m0.092s

जैसा कि आप ऊपर देख सकते हैं, समय लेने वाला कदम डेटा को प्रिंट कर रहा है। यदि आप बस एक नई फ़ाइल पर पुनर्निर्देशित करते हैं और उसके माध्यम से grep करते हैं तो यह बहुत तेज होता है जब केवल एक बार फाइल पढ़ते हैं।


और अनुरोध के रूप में, यहां एक स्ट्रिंग के साथ:

 $ time (log=$(tail -n 1000000 foo); grep 'true' <<< $log | wc -l; 
                                     grep 'false' <<< $log | wc -l  )
1
1

real    0m7.574s
user    0m7.092s
sys     0m0.516s

यह एक और भी धीमा है, संभवतः क्योंकि यहां स्ट्रिंग एक लंबी लाइन पर सभी डेटा को समाप्‍त कर रही है और यह धीमा हो जाएगा grep:

$ tail -n 1000000 foo | (time grep -c 'true')
666666

real    0m0.500s
user    0m0.472s
sys     0m0.000s

$ tail -n 1000000 foo | perl -pe 's/\n/ /' | (time grep -c 'true')
1

real    0m1.053s
user    0m0.048s
sys     0m0.068s

यदि चर को उद्धृत किया जाता है ताकि कोई विभाजन न हो, तो चीजें थोड़ी तेज़ होती हैं:

 $ time (log=$(tail -n 1000000 foo); grep 'true' <<< "$log" | wc -l; 
                                     grep 'false' <<< "$log" | wc -l  )
666666
333333

real    0m6.545s
user    0m6.060s
sys     0m0.548s

लेकिन फिर भी धीमा है क्योंकि दर सीमित कदम डेटा को प्रिंट कर रहा है।


आप कोशिश क्यों नहीं करते हैं <<<यह देखना दिलचस्प होगा कि क्या इससे कोई फर्क पड़ता है।
ग्रीम

3

मैं इस पर भी गया था ... सबसे पहले, मैंने फ़ाइल बनाई:

printf '"success": "true"
        "success": "true"
        "success": "false"
        %.0b' `seq 1 500000` >|/tmp/log

यदि आप ऊपर स्वयं चलाते हैं, तो आपको लाइनों के /tmp/logसाथ "success": "true"लाइनों के अनुपात में 2: 1 अनुपात के साथ 1.5 मिलियन बिलियन तक आना चाहिए "success": "false"

अगली चीज जो मैंने की वह कुछ परीक्षण चलाए गए थे। मैंने सभी परीक्षणों को एक प्रॉक्सी के माध्यम से चलाया, shताकि timeकेवल एक ही प्रक्रिया देखनी पड़े - और इसलिए पूरी नौकरी के लिए एक ही परिणाम दिखा सके।

यह सबसे तेज़ प्रतीत होता है, भले ही यह एक दूसरी फ़ाइल डिस्क्रिप्टर जोड़ता है और tee,हालांकि मुझे लगता है कि मैं समझा सकता हूं कि क्यों:

    time sh <<-\CMD
        . <<HD /dev/stdin | grep '"success": "true"' | wc -l
            tail -n 1000000 /tmp/log | { tee /dev/fd/3 |\
                grep '"success": "false"' |\
                    wc -l 1>&2 & } 3>&1 &
        HD
    CMD
666666
333334
sh <<<''  0.11s user 0.08s system 84% cpu 0.224 total

यहाँ आपका पहला है:

    time sh <<\CMD
        tail -n 1000000 /tmp/log | grep '"success": "true"' | wc -l
        tail -n 1000000 /tmp/log | grep '"success": "false"' | wc -l
    CMD

666666
333334
sh <<<''  0.31s user 0.17s system 148% cpu 0.323 total

और आपका दूसरा:

    time sh <<\CMD
        log=$(tail -n 1000000 /tmp/log)
        echo "$log" | grep '"success": "true"' | wc -l
        echo "$log" | grep '"success": "false"' | wc -l
    CMD
666666
333334
sh <<<''  2.12s user 0.46s system 108% cpu 2.381 total

आप देख सकते हैं कि मेरे परीक्षणों में गति में 3 * से अधिक का अंतर था जब आपने इसे एक चर में पढ़ा था।

मुझे लगता है कि इसका एक हिस्सा चर को खोलना और खोलकर संभालना होता है जब इसे पढ़ा जा रहा हो - यह फाइल नहीं है।

एक here-documentतरफ, सभी इरादों और उद्देश्यों के लिए, एक है file-file descriptor, वैसे भी। और जैसा कि हम सभी जानते हैं - यूनिक्स फाइलों के साथ काम करता है।

मेरे बारे here-docsमें जो सबसे दिलचस्प है, वह यह है कि आप उन्हें -file-descriptors सीधे के रूप में |pipe- और उन्हें निष्पादित कर सकते हैं। यह बहुत आसान है क्योंकि यह आपको अपनी |pipeपसंद को इंगित करने में थोड़ी अधिक स्वतंत्रता देता है ।

मैं करना पड़ा क्योंकि पहले खाती है और वहाँ के लिए कुछ भी नहीं दूसरे के लिए छोड़ दिया पढ़ने के लिए। लेकिन जब से मैं इसे में लाया और इसे फिर से उठाया इसे पारित करने के लिए ज्यादा कोई फर्क नहीं पड़ा । यदि आप अन्य लोगों की सलाह के अनुसार उपयोग करते हैं:teetailgrephere-doc |pipe|piped/dev/fd/3>&1 stdout,grep -c

    time sh <<-\CMD
        . <<HD /dev/stdin | grep -c '"success": "true"'
            tail -n 1000000 /tmp/log | { tee /dev/fd/3 |\
                grep -c '"success": "false"' 1>&2 & } 3>&1 &
        HD
    CMD
666666
333334
sh <<<''  0.07s user 0.04s system 62% cpu 0.175 total

यह और भी तेज है।

लेकिन जब मैंने उसे बिना चला मैं सफलतापूर्वक पृष्ठभूमि पहली प्रक्रिया उन्हें पूरी तरह से समवर्ती चलाने के लिए नहीं कर सकते। यहाँ यह पूरी तरह से इसकी पृष्ठभूमि के बिना है:. sourcingheredoc

    time sh <<\CMD
        tail -n 1000000 /tmp/log | { tee /dev/fd/3 |\
            grep -c '"success": "true"' 1>&2 & } 3>&1 |\
                grep -c '"success": "false"'
    CMD
666666
333334
sh <<<''  0.10s user 0.08s system 109% cpu 0.165 total

लेकिन जब मैं जोड़ता हूं &:

    time sh <<\CMD
        tail -n 1000000 /tmp/log | { tee /dev/fd/3 |\
            grep -c '"success": "true"' 1>&2 & } 3>&1 & |\
                grep -c '"success": "false"'
    CMD
sh: line 2: syntax error near unexpected token `|'

फिर भी, अंतर केवल एक सेकंड के कुछ सौवें हिस्से के लिए प्रतीत होता है, कम से कम मेरे लिए, इसलिए इसे अपनी इच्छानुसार लें।

वैसे भी, इसका कारण तेजी से चलता teeहै क्योंकि दोनों grepsएक ही समय पर एक ही साथ चलते हैं, जो tail. teeहमारे लिए फ़ाइल को डुप्लिकेट करता है और इसे दूसरी grepप्रक्रिया में विभाजित कर देता है, जो सभी इन-स्ट्रीम में शुरू होता है - सब कुछ शुरू से अंत तक चलता है, इसलिए वे सभी एक ही समय के आसपास खत्म, भी।

इसलिए अपने पहले उदाहरण पर वापस जा रहे हैं:

    tail | grep | wc #wait til finished
    tail | grep | wc #now we're done

और आपका दूसरा:

    var=$( tail ) ; #wait til finished
    echo | grep | wc #wait til finished
    echo | grep | wc #now we're done

लेकिन जब हम अपने इनपुट को विभाजित करते हैं और अपनी प्रक्रियाओं को समवर्ती रूप से चलाते हैं:

          3>&1  | grep #now we're done
              /        
    tail | tee  #both process together
              \  
          >&1   | grep #now we're done

1
+1 लेकिन आपका अंतिम परीक्षण एक सिंटैक्स त्रुटि के साथ हुआ, मुझे नहीं लगता कि समय सही है :)
टेराडॉन

@terdon वे गलत हो सकते हैं - मैं इशारा कर रहा था कि यह मर गया। मैंने & और नहीं के बीच का अंतर दिखाया - जब आप इसे जोड़ते हैं, तो शेल परेशान हो जाता है। लेकिन मैंने बहुत कॉपी / पेस्ट किया, इसलिए मैंने एक या दो को गड़बड़ कर दिया, लेकिन मुझे लगता है कि वे
बिलकुल

sh: पंक्ति 2: अप्रत्याशित टोकन के पास सिंटैक्स त्रुटि `| '
terdon

@terdon हाँ कि - "मैं उन्हें सफलतापूर्वक पूरी तरह से चलाने के लिए पहली प्रक्रिया की पृष्ठभूमि नहीं दे सकता। देखिए?" पहले वाले को बैकग्राउंड नहीं किया गया है, लेकिन जब मैं ऐसा करने के प्रयास में जोड़ता हूं "अप्रत्याशित टोकन।" जब मैं । स्रोत heredoc मैं & का उपयोग कर सकते हैं।
mikeserv
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.