2010-12-20 15 views
18

(संभवतः संबंधित Do some programs not accept process substitution for input files? करने के लिए)बैश प्रक्रिया प्रतिस्थापन और सिंकिंग

कुछ बैश इकाई परीक्षण स्क्रिप्ट में मैं लॉग इन करने के और प्रदर्शन stdout और एक आदेश के stderr निम्नलिखित चाल का उपयोग कर रहा:

command > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2) 

यह प्रक्रिया stdout के लिए कुछ आउटपुट उत्पन्न करती है, इसलिए $stdoutF फ़ाइल कुछ डेटा प्राप्त करती है। तब मैं एक और आदेश जो उत्पादन किसी भी डेटा नहीं चलाएँ:

diff -r "$source" "$target" > >(tee "${stdoutF}") 2> >(tee "${stderrF}" >&2) 

हालांकि, यह की तरह इस प्रक्रिया को हमेशा से पहले शून्य के लिए परीक्षण चलाया जाता है सफलतापूर्वक खत्म (shunit-ng उपयोग करते हुए) नहीं लगती है:

assertNull 'Unexpected output to stdout' "$(<"$stdoutF")" 

एक 100 रन परीक्षण में यह 25 बार विफल रहा।

यह शून्य के लिए फ़ाइल परीक्षण से पहले sync कॉल करने के लिए पर्याप्त होना चाहिए:

sync 
assertNull 'Unexpected output to stdout' "$(<"$stdoutF")" 

... और/या यह आदेशों के अनुक्रम मजबूर कर काम करना चाहिए:

diff -r "$source" "$target" \ 
> >(tee "${stdoutF}"; assertNull 'Unexpected output to stdout' "$(<"$stdoutF")") 
2> >(tee "${stderrF}" >&2) 

। .. और/या tee यह किसी फ़ाइल के बजाय सीधे assertNull पर संभव है?

अद्यतन: sync उत्तर नहीं है - नीचे गिल्स की प्रतिक्रिया देखें।

अपडेट 2: Save stdout, stderr and stdout+stderr synchronously पर चर्चा की गई। उत्तर के लिए धन्यवाद!

उत्तर

27

बैश में, एक प्रक्रिया प्रतिस्थापन प्रतिस्थापन आदेश foo > >(bar) खत्म जवाब देने के लिए sync के स्थान पर एक sleep 5 या whatnot डालें। (यह दस्तावेज में चर्चा नहीं कर रहा है।) आप तुरंत

: > >(sleep 1; echo a) 

यह आदेश रिटर्न के साथ इस जाँच कर सकते हैं, तो a प्रिंट एसिंक्रोनस रूप से एक सेकंड बाद में।

आपके मामले में, tee कमांड command पूर्ण होने के बाद समाप्त करने के लिए केवल एक छोटा सा समय लेता है। sync जोड़ने से tee पूरा करने के लिए पर्याप्त समय दिया गया है, लेकिन यह sleep जोड़ने से अधिक की दौड़ स्थिति को नहीं हटाता है, यह केवल दौड़ को प्रकट करने की अधिक संभावना नहीं बनाता है।

अधिक आम तौर पर, sync में कोई आंतरिक रूप से देखने योग्य प्रभाव नहीं होता है: यदि आप डिवाइस को एक्सेस करना चाहते हैं तो यह केवल एक फर्क पड़ता है जहां आपके फाइल सिस्टम एक अलग ऑपरेटिंग सिस्टम इंस्टेंस के तहत संग्रहीत होते हैं। स्पष्ट शब्दों में, यदि आपका सिस्टम शक्ति खो देता है, तो आपके द्वारा रीबूट करने के बाद अंतिम sync से पहले लिखा गया डेटा उपलब्ध होने की गारंटी है।

रेस स्थिति को दूर करने के लिए के रूप में, यहाँ संभव तरीकों में से कुछ हैं:

  • स्पष्ट रूप से सभी एवजी प्रक्रियाओं सिंक्रनाइज़।

    mkfifo sync.pipe 
    command > >(tee -- "$stdoutF"; echo >sync.pipe) 
         2> >(tee -- "$stderrF"; echo >sync.pipe) 
    read line < sync.pipe; read line < sync.pipe 
    
  • बजाय $stdoutF और $stderrF पुन: उपयोग के प्रत्येक आदेश के लिए एक अलग अस्थायी फ़ाइल नाम का उपयोग करें, और लागू जिनके लिए अस्थायी फ़ाइल हमेशा नव निर्मित किया गया है।

  • प्रक्रिया प्रतिस्थापन पर छोड़ दें और इसके बजाय पाइप का उपयोग करें।

    { { command | tee -- "$stdoutF" 1>&3; } 2>&1 \ 
          | tee -- "$stderrF" 1>&2; } 3>&1 
    

    आप आदेश की वापसी स्थिति की जरूरत है, बैश ${PIPESTATUS[0]} में कहते हैं।

    { { command | tee -- "$stdoutF" 1>&3; exit ${PIPESTATUS[0]}; } 2>&1 \ 
          | tee -- "$stderrF" 1>&2; } 3>&1 
    if [ ${PIPESTATUS[0]} -ne 0 ]; then echo command failed; fi 
    
+0

धन्यवाद इस भयानक उत्तर के लिए, यह सिर्फ मेरे लिए एक बेहतर व्यवस्थापक बना दिया है! –

-1

जैसे ही foo खत्म के रूप में अपने आखिरी सवाल

1

मैं कभी कभी एक गार्ड रख:

: > >(sleep 1; echo a; touch guard) \ 
    && while true; do 
    [ -f "guard" ] && { rm guard; break; } 
    sleep 0.2 
    done  
संबंधित मुद्दे