प्रक्रिया प्रतिस्थापन आउटपुट आदेश से बाहर है


16

echo one; echo two > >(cat); echo three; 

कमांड अप्रत्याशित आउटपुट देता है।

मैंने इसे पढ़ा: कैसे प्रक्रिया प्रतिस्थापन बैश में कार्यान्वित किया जाता है? और इंटरनेट पर प्रक्रिया प्रतिस्थापन के बारे में कई अन्य लेख, लेकिन समझ में नहीं आता कि यह इस तरह क्यों व्यवहार करता है।

अपेक्षित उत्पादन:

one
two
three

वास्तविक उत्पादन:

prompt$ echo one; echo two > >(cat); echo three;
one
three
prompt$ two

साथ ही, यह दो आदेश मेरे दृष्टिकोण से समतुल्य होने चाहिए, लेकिन वे नहीं हैं:

##### first command - the pipe is used.
prompt$ seq 1 5 | cat
1
2
3
4
5
##### second command - the process substitution and redirection are used.
prompt$ seq 1 5 > >(cat)
prompt$ 1
2
3
4
5

मुझे क्यों लगता है, वे एक ही होना चाहिए? क्योंकि, दोनों seqआउटपुट को catअनाम पाइप के माध्यम से इनपुट से जोड़ते हैं - विकिपीडिया, प्रक्रिया प्रतिस्थापन

प्रश्न: यह इस तरह से व्यवहार क्यों करता है? मेरी गलती कहाँ है? व्यापक उत्तर वांछित है ( bashहुड के नीचे यह कैसे करता है, इसकी व्याख्या के साथ )।


2
यहां तक ​​कि अगर यह पहली नजर में इतना स्पष्ट नहीं है, तो यह वास्तव में आदेश प्रतिस्थापन में प्रक्रिया के प्रतिस्थापन के लिए
बाट का

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

जवाबों:


21

हां, bashजैसे कि ksh(जहां से फीचर आता है), प्रक्रिया प्रतिस्थापन के अंदर की प्रक्रियाओं का इंतजार नहीं किया जाता है (स्क्रिप्ट में अगली कमांड चलाने से पहले)।

एक के लिए <(...)एक, आमतौर पर ठीक है कि के रूप में:

cmd1 <(cmd2)

शेल की प्रतीक्षा की जाएगी cmd1और cmd1आमतौर पर cmd2इसके पढ़े जाने के पुण्य से इंतजार किया जाएगा, जब तक कि प्रतिस्थापित होने वाले पाइप पर फ़ाइल का अंत नहीं हो जाता है, और अंत में फ़ाइल आमतौर पर तब होती है जब उसकी cmd2मृत्यु हो जाती है। यही कारण है कि इसी कारण से कई गोले (नहीं है bash) के लिए इंतज़ार कर परेशान नहीं है cmd2में cmd2 | cmd1

के लिए cmd1 >(cmd2), हालांकि, कि आम तौर पर ऐसा नहीं है, के रूप में यह और भी है cmd2कि आम तौर पर के लिए इंतजार कर रहा है cmd1के बाद आम तौर पर बाहर हो जाएगा वहाँ बहुत।

इसमें तय किया गया है zshकि cmd2वहां इंतजार किया जाता है (लेकिन अगर आप इसे नहीं लिखते हैं cmd1 > >(cmd2)और cmd1बिल्ट नहीं है, तो {cmd1} > >(cmd2)इसके बजाय दस्तावेज़ के रूप में उपयोग करें )।

kshडिफ़ॉल्ट रूप से प्रतीक्षा नहीं करता है, लेकिन आपको इसे waitबेसिन के साथ प्रतीक्षा करने देता है (यह भी पीआईडी ​​को उपलब्ध कराता है $!, हालांकि यह आपके लिए मदद नहीं करता है cmd1 >(cmd2) >(cmd3))

rc( cmd1 >{cmd2}सिंटैक्स के साथ ), उसी तरह जैसे kshआप सभी पृष्ठभूमि प्रक्रियाओं के साथ प्राप्त कर सकते हैं $apids

es(के साथ भी cmd1 >{cmd2}) की cmd2तरह इंतजार कर रहा है zsh, और प्रक्रिया पुनर्निर्देशन cmd2में भी इंतजार कर रहा है <{cmd2}

bashकी उप-धारा cmd2(या इससे अधिक बिल्कुल उप-भाग के रूप में) बनाता है, क्योंकि यह उस उप-भाग cmd2की एक बाल प्रक्रिया में चलता है, भले ही यह वहां अंतिम कमांड हो) उपलब्ध है $!, लेकिन आपको इसके लिए प्रतीक्षा नहीं करने देता।

यदि आपको उपयोग करना है bash, तो आप एक कमांड का उपयोग करके समस्या के आसपास काम कर सकते हैं जो दोनों कमांड के लिए इंतजार करेगा:

{ { cmd1 >(cmd2); } 3>&1 >&4 4>&- | cat; } 4>&1

यह दोनों बनाता है cmd1और cmd2उनके fd 3 एक पाइप के लिए खुला है। catअंत फ़ाइल दूसरे छोर पर के लिए इंतजार करेंगे, तो आम तौर पर केवल बाहर निकलने जब दोनों जाएगा cmd1और cmd2मर चुके हैं। और शेल उस catकमांड का इंतजार करेगा । आप यह देख सकते हैं कि सभी पृष्ठभूमि प्रक्रियाओं की समाप्ति को पकड़ने के लिए एक जाल के रूप में (आप इसे पृष्ठभूमि में शुरू की गई अन्य चीजों के लिए उपयोग कर सकते हैं &, जैसे कि सहपाठियों या यहां तक ​​कि यह कि वे स्वयं पृष्ठभूमि प्रदान करते हैं, वे अपने सभी फ़ाइल विवरणों को बंद नहीं करते हैं जैसे कि डेमॉन आमतौर पर करते हैं। )।

ध्यान दें कि उपर्युक्त उस व्यर्थ उपशमन प्रक्रिया के लिए धन्यवाद, यह काम करता है भले ही cmd2इसकी fd 3 बंद हो जाए (कमांड आमतौर पर ऐसा नहीं करते हैं, लेकिन कुछ पसंद करते हैं sudoया sshकरते हैं)। भविष्य के संस्करण bashअंततः अन्य गोले की तरह वहाँ अनुकूलन कर सकते हैं। फिर आपको कुछ इस तरह की आवश्यकता होगी:

{ { cmd1 >(sudo cmd2; exit); } 3>&1 >&4 4>&- | cat; } 4>&1

यह सुनिश्चित करने के लिए कि अभी भी एक अतिरिक्त शेल प्रक्रिया है, जिसमें fd 3 उस sudoकमांड की प्रतीक्षा कर रहा है ।

ध्यान दें कि catकुछ भी नहीं पढ़ेगा (क्योंकि प्रक्रियाएं अपने fd 3 पर नहीं लिखती हैं)। यह सिर्फ सिंक्रनाइज़ेशन के लिए है। यह सिर्फ एक read()सिस्टम कॉल करेगा जो अंत में कुछ भी नहीं के साथ वापस आ जाएगा।

आप वास्तव catमें पाइप सिंक्रनाइज़ेशन करने के लिए कमांड प्रतिस्थापन का उपयोग करके चलने से बच सकते हैं :

{ unused=$( { cmd1 >(cmd2); } 3>&1 >&4 4>&-); } 4>&1

इस बार, यह इसके बजाय शेल catहै जो पाइप से पढ़ रहा है जिसका दूसरा छोर fd 3 cmd1और पर खुला है cmd2। हम एक चर असाइनमेंट का उपयोग कर रहे हैं ताकि बाहर निकलने की स्थिति cmd1उपलब्ध हो $?

या आप हाथ से प्रक्रिया प्रतिस्थापन कर सकते हैं, और फिर आप अपने सिस्टम का उपयोग भी कर सकते हैं shजो कि मानक शेल सिंटैक्स बन जाएगा:

{ cmd1 /dev/fd/3 3>&1 >&4 4>&- | cmd2 4>&-; } 4>&1

हालाँकि, जैसा कि पहले नोट किया गया था कि सभी shकार्यान्वयन समाप्त cmd1होने के बाद इंतजार नहीं करेंगे cmd2(हालांकि यह दूसरे तरीके से बेहतर है)। उस समय, $?के बाहर निकलने की स्थिति शामिल है cmd2; हालांकि bashऔर zshमेकअप cmd1के बाहर निकलने में उपलब्ध स्थिति ${PIPESTATUS[0]}और $pipestatus[1]क्रमश: (यह भी देखें pipefailकुछ गोले में विकल्प तो $?पिछले अलावा अन्य पाइप घटकों की विफलता रिपोर्ट कर सकते हैं)

ध्यान दें कि yashइसकी प्रक्रिया पुनर्निर्देशन सुविधा के साथ समान समस्याएं हैं। वहाँ cmd1 >(cmd2)लिखा जाएगा cmd1 /dev/fd/3 3>(cmd2)। लेकिन cmd2इसके लिए इंतजार नहीं किया जाता है और आप इसके waitलिए प्रतीक्षा करने के लिए उपयोग नहीं कर सकते हैं और इसका पीड $!चर में भी उपलब्ध नहीं कराया गया है। आप के लिए के रूप में चारों ओर एक ही काम का उपयोग करेंगे bash


सबसे पहले, मैंने कोशिश की echo one; { { echo two > >(cat); } 3>&1 >&4 4>&- | cat; } 4>&1; echo three;, फिर इसे सरल किया echo one; echo two > >(cat) | cat; echo three;और यह सही क्रम में मूल्यों को आउटपुट करता है। क्या यह सब वर्णनात्मक जोड़तोड़ 3>&1 >&4 4>&-आवश्यक है? इसके अलावा, मुझे यह नहीं मिलता है >&4 4>&- हम stdoutचौथी एफडी पर पुनर्निर्देशित करते हैं , फिर चौथे एफडी को बंद करते हैं, फिर 4>&1इसे फिर से उपयोग करते हैं। इसकी आवश्यकता क्यों है और यह कैसे काम करता है? हो सकता है, मुझे इस विषय पर नया प्रश्न बनाना चाहिए?
मिनीमैक्स

1
@MiniMax, लेकिन वहाँ है, तो आप के लिए stdout प्रभावित कर रहे हैं cmd1और cmd2, फ़ाइल वर्णनकर्ता के साथ थोड़ा नृत्य के साथ बिंदु मूल लोगों को बहाल करने और के लिए केवल अतिरिक्त पाइप का उपयोग कर रहा है इंतजार कर के बजाय भी आदेशों की उत्पादन channeling।
स्टीफन चेज़लस

@MiniMax मुझे समझने में थोड़ा समय लगा, मुझे इससे पहले इतने निचले स्तर पर पाइप नहीं मिले थे। सबसे दायीं ओर 4>&1बाहरी ब्रेसिज़ आदेश सूची के लिए एक फ़ाइल वर्णनकर्ता (FD) 4 बनाता है, और यह बाहरी ब्रेसिज़ 'stdout में बराबर बना देता है। बाहरी ब्रेसिज़ से जुड़ने के लिए आंतरिक ब्रेसिज़ में स्वचालित रूप से स्टड / स्टडआउट / स्टेडर होता है। हालाँकि, 3>&1fd 3 बाहरी ब्रेसेस के स्टड से जुड़ता है। >&4आंतरिक ब्रेसिज़ के स्टडआउट को बाहरी ब्रेसिज़ fd 4 से जोड़ता है (वह जिसे हमने पहले बनाया था)। 4>&-आंतरिक ब्रेड्स से fd 4 को बंद कर देता है (चूंकि आंतरिक ब्रेसेस का स्टडआउट पहले से ही ब्रेसेस की fd 4 से जुड़ा हुआ है)।
निकोलस पिपिटोन

@MiniMax भ्रमित करने वाला हिस्सा दाएं-से-बाएं हिस्सा था, 4>&1पहले निष्पादित हो जाता है, दूसरे रीडायरेक्ट से पहले, ताकि आप "फिर से उपयोग न करें 4>&1"। कुल मिलाकर, आंतरिक ब्रेस अपने स्टडआउट को डेटा भेज रहा है, जो कि जो भी fd 4 दिया गया था, उसके साथ ओवरराइट किया गया था। Fd 4 जो आंतरिक ब्रेसिज़ दिया गया था, बाहरी ब्रेसिज़ fd 4 है, जो बाहरी ब्रेसेस के मूल स्टडआउट के बराबर है।
निकोलस पिपिटोन

बैश यह महसूस करता है कि 4>5इसका मतलब है "4 चला जाता है 5", लेकिन वास्तव में "fd 4 को fd 5 के साथ ओवरराइट किया गया है"। और निष्पादन से पहले, fd 0/1/2 ऑटो जुड़ा हुआ है (बाहरी शेल के किसी भी fd के साथ), और आप अपनी इच्छानुसार उन्हें ओवरराइट कर सकते हैं। यह कम से कम बैश प्रलेखन की मेरी व्याख्या है। यदि आप इस से बाहर कुछ और समझ , lmk।
निकोलस पिपिटोन

4

आप दूसरी कमांड को दूसरे में पाइप कर सकते हैं cat, जो तब तक इंतजार करेगी जब तक उसका इनपुट पाइप बंद नहीं हो जाता। उदाहरण के लिए:

prompt$ echo one; echo two > >(cat) | cat; echo three;
one
two
three
prompt$

छोटा और सरल।

==========

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

जब आपके पास होता है echo two > >(cat); echo three, तो >(cat)इंटरेक्टिव शेल द्वारा बंद कर दिया जाता है, और स्वतंत्र रूप से चलता है echo two। इस प्रकार, echo twoखत्म, और फिर echo threeनिष्पादित हो जाता है, लेकिन >(cat)खत्म होने से पहले । जब bashउसे >(cat)यह अपेक्षा नहीं थी कि बाद में डेटा कब मिलता है (मिलीसेकेंड के एक जोड़े), तो यह आपको उस त्वरित स्थिति की तरह देता है, जहां आपको टर्मिनल पर वापस जाने के लिए न्यूलाइन मारना होगा (जैसे कि कोई अन्य उपयोगकर्ता mesgआपको एड करता है)।

हालाँकि, दिए गए echo two > >(cat) | cat; echo three, दो उपधाराएँ स्पॉन के रूप में ( |प्रतीक के प्रलेखन के अनुसार ) हैं।

A नाम का एक सबस्क्रिप्शन है echo two > >(cat), और B नाम का एक सबस्क्रिप्शन है cat। A स्वचालित रूप से B से जुड़ा है (A का स्टडआउट B का स्टडिन है)। फिर, echo twoऔर >(cat)निष्पादित करना शुरू करें। >(cat)का स्टडआउट A के स्टडआउट पर सेट है, जो B के स्टड के बराबर है। echo twoखत्म होने के बाद , ए बाहर निकलता है, अपने स्टडआउट को बंद कर देता है। हालांकि, >(cat)अभी भी बी के स्टड के संदर्भ को पकड़े हुए है। दूसरा catस्टड बी के स्टड को पकड़े हुए है, और catजब तक कि यह ईओएफ को नहीं देखता है तब तक बाहर नहीं निकलेगा। एक ईओएफ केवल तभी दिया जाता है जब किसी के पास अब >(cat)लिखित मोड में कोई फ़ाइल नहीं होती है, इसलिए दूसरे को रोक रहा है cat। B उस दूसरे पर प्रतीक्षा कर रहा है catecho twoबाहर निकलने के बाद से >(cat)अंततः ईओएफ मिलता है, इसलिए>(cat)इसके बफर को फ्लश करता है और बाहर निकलता है। अब कोई भी B / सेकंड catके स्टड को नहीं पकड़ रहा है, इसलिए दूसरा catEOF पढ़ता है (B अपने स्टिन को बिल्कुल नहीं पढ़ रहा है, यह ध्यान नहीं देता है)। यह ईओएफ दूसरे catको अपने बफर को फ्लश करने, अपने स्टडआउट को बंद करने और बाहर निकलने का कारण बनता है , और फिर बी बाहर निकलता है क्योंकि catबाहर निकलता है और बी इंतजार कर रहा था cat

इसके बारे में एक चेतावनी यह है कि bash भी एक उपसमूह को पैदा करता है >(cat)! इस वजह से, आप देखेंगे

echo two > >(sleep 5) | cat; echo three

अभी भी निष्पादित करने से पहले 5 सेकंड प्रतीक्षा करेंगे echo three, भले ही sleep 5बी के स्टडिन को पकड़ना नहीं है। इसका कारण यह है कि सी के लिए छिपा हुआ एक सबस्क्रिप्शन सी >(sleep 5)प्रतीक्षा में है sleep, और सी बी के स्टड को पकड़ रहा है। आप देख सकते हैं कैसे

echo two > >(exec sleep 5) | cat; echo three

हालांकि इंतजार नहीं करेंगे, क्योंकि sleepबी के स्टडिन को पकड़ नहीं है, और बी के स्टडिन को पकड़ने के लिए कोई भूत उप सी नहीं है (निष्पादन सी ​​को बदलने के लिए नींद को मजबूर करेगा, क्योंकि सी का इंतजार करने और बनाने के लिए विरोध किया गया है sleep)। इस चेतावनी के बावजूद,

echo two > >(exec cat) | cat; echo three

अभी भी ठीक से कार्य करेगा, जैसा कि पहले बताया गया है।


जैसा कि मेरे जवाब में टिप्पणियों में @MiniMax के साथ रूपांतरण में उल्लेख किया गया है, हालांकि यह कमांड के स्टैडआउट को प्रभावित करने का नकारात्मक पहलू है और इसका मतलब है कि आउटपुट को अतिरिक्त समय में पढ़ना और लिखना होगा।
स्टीफन चेजालस

स्पष्टीकरण सटीक नहीं है। Aमें catspawned के लिए इंतजार नहीं कर रहा है >(cat)। जैसा कि मैंने अपने उत्तर में उल्लेख किया है, कारण है कि 5 सेकंड के बाद echo two > >(sleep 5 &>/dev/null) | cat; echo threeआउटपुट क्यों threeहोता है क्योंकि bashकचरे के वर्तमान संस्करण >(sleep 5)उस प्रक्रिया में एक अतिरिक्त शेल प्रक्रिया की प्रतीक्षा करते हैं sleepऔर उस प्रक्रिया में अभी भी स्टडआउट है pipeजो दूसरे catको समाप्त होने से रोकता है । यदि आप इसे echo two > >(exec sleep 5 &>/dev/null) | cat; echo threeउस अतिरिक्त प्रक्रिया को खत्म करने के साथ प्रतिस्थापित करते हैं , तो आप पाएंगे कि यह सीधे वापस लौटता है।
स्टीफन चेजलस

यह एक नेस्टेड सबस्क्रिप्शन बनाता है? मैं इसे लागू करने की कोशिश कर रहा हूँ ताकि यह पता लगाया जा सके कि मुझे पूरा यकीन है echo two > >(sleep 5 &>/dev/null)कि कम से कम इसका अपना सब-डिमांड मिल जाएगा। क्या यह एक गैर-प्रलेखित कार्यान्वयन विवरण है जिसके कारण sleep 5इसकी अपनी सदस्यता भी मिल सकती है? यदि इसे प्रलेखित किया जाता है, तो इसे कम वर्णों के साथ करने का एक वैध तरीका होगा (जब तक कि एक तंग लूप न हो, मुझे नहीं लगता कि किसी को एक उपधारा, या एक बिल्ली के साथ प्रदर्शन की समस्याओं को नोटिस किया जाएगा)। यदि यह प्रलेखित नहीं है, तो चीर, अच्छी हैक हालांकि, भविष्य के संस्करणों पर काम नहीं करेगा।
निकोलस पिपिटोन

$(...), <(...)वास्तव में एक सबशेल को शामिल करते हैं, लेकिन ksh93 या zsh उसी सबस्क्रिप्शन में अंतिम कमांड को चलाएगा, bashयही वजह है कि पाइप को खुला sleepनहीं रखते हुए पाइप को खुला रखते हुए एक अन्य प्रक्रिया है । भविष्य के संस्करण bashएक समान अनुकूलन को लागू कर सकते हैं।
स्टीफन चेज़लस

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