जवाबों:
सह-प्रक्रिया एक kshविशेषता (पहले से ही ksh88) है। zshशुरुआत (90 के दशक) से इसकी विशेषता थी, जबकि इसे केवल (2009) bashमें जोड़ा गया है 4.0।
हालांकि, 3 गोले के बीच व्यवहार और इंटरफ़ेस काफी अलग है।
विचार समान है, हालांकि: यह पृष्ठभूमि में एक नौकरी शुरू करने की अनुमति देता है और इसे इनपुट भेजने में सक्षम है और नामित पाइप का सहारा लिए बिना इसका आउटपुट पढ़ सकता है।
कुछ प्रणालियों पर ksh93 के हाल के संस्करणों के साथ अधिकांश गोले और सॉकेटपेयर के साथ अनाम पाइपों के साथ किया जाता है।
में a | cmd | b, इसके आउटपुट aको डेटा को पढ़ता है cmdऔर bपढ़ता है। cmdसह-प्रक्रिया के रूप में चलने से शेल दोनों aऔर हो सकता है b।
में ksh, आप एक कोप्रोसेस शुरू करते हैं:
cmd |&
आप करने के लिए डेटा फ़ीड cmdतरह बातें करने से:
echo test >&p
या
print -p test
और cmdचीजों के साथ आउटपुट पढ़ें :
read var <&p
या
read -p var
cmdकिसी भी पृष्ठभूमि कार्य के रूप में शुरू कर दिया है, आप उपयोग कर सकते हैं fg, bg, killउस पर और द्वारा यह उल्लेख %job-numberया के माध्यम से $!।
पाइप के लेखन अंत को बंद करने के लिए cmd, आप कर सकते हैं:
exec 3>&p 3>&-
और दूसरे पाइप के रीडिंग एंड को बंद करने के लिए (जो cmdलिख रहा है):
exec 3<&p 3<&-
जब तक आप पहली बार पाइप फ़ाइल डिस्क्रिप्टर को कुछ अन्य fds में सहेजते हैं, तब तक आप दूसरी सह-प्रक्रिया शुरू नहीं कर सकते। उदाहरण के लिए:
tr a b |&
exec 3>&p 4<&p
tr b c |&
echo aaa >&3
echo bbb >&p
में zsh, सह-प्रक्रिया लगभग उन लोगों के समान है ksh। एकमात्र वास्तविक अंतर यह है कि कीवर्ड के zshसाथ सह-प्रक्रिया शुरू की जाती है coproc।
coproc cmd
echo test >&p
read var <&p
print -p test
read -p var
करते हुए:
exec 3>&p
नोट: यह coprocफ़ाइल डिस्क्रिप्टर को fd 3(जैसे ksh) में स्थानांतरित नहीं करता है , लेकिन इसे डुप्लिकेट करता है। तो, वहाँ कोई स्पष्ट रास्ता खिला या पढ़ने पाइप बंद करने के लिए, अन्य शुरू कर एक और coproc ।
उदाहरण के लिए, खिला अंत को बंद करने के लिए:
coproc tr a b
echo aaaa >&p # send some data
exec 4<&p # preserve the reading end on fd 4
coproc : # start a new short-lived coproc (runs the null command)
cat <&4 # read the output of the first coproc
पाइप आधारित सह-प्रक्रियाओं के अलावा, zsh(2000 में जारी 3.1.6-dev19 के बाद से) में छद्म ट्टी आधारित निर्माण जैसे हैं expect। अधिकांश कार्यक्रमों के साथ बातचीत करने के लिए, ksh- शैली की सह-प्रक्रियाएं काम नहीं करेंगी, क्योंकि प्रोग्राम तब बफरिंग शुरू करते हैं जब उनका आउटपुट एक पाइप होता है।
यहाँ कुछ उदाहरण हैं।
सह-प्रक्रिया शुरू करें x:
zmodload zsh/zpty
zpty x cmd
(यहां, cmdएक साधारण आदेश है। लेकिन आप कट्टर evalकार्यों को या कार्यों के साथ कर सकते हैं ।)
एक सह-प्रक्रिया डेटा फ़ीड:
zpty -w x some data
सह-प्रक्रिया डेटा (सरलतम स्थिति में) पढ़ें:
zpty -r x var
जैसे expect, यह किसी दिए गए पैटर्न से मेल खाते हुए सह-प्रक्रिया से कुछ आउटपुट की प्रतीक्षा कर सकता है।
बैश सिंटैक्स बहुत नया है, और हाल ही में ksh93, bash और zsh में जोड़े गए एक नए फीचर के शीर्ष पर बनाता है। यह डायनामिक रूप से आवंटित फ़ाइल डिस्क्रिप्टर की 10 से ऊपर की हैंडलिंग की अनुमति देने के लिए एक सिंटैक्स प्रदान करता है।
bashएक मूल coproc वाक्यविन्यास, और एक विस्तारित एक प्रदान करता है।
सह-प्रक्रिया शुरू करने के लिए मूल वाक्यविन्यास ऐसा लगता है zsh:
coproc cmd
में kshया zsh, करने के लिए और सह प्रक्रिया से पाइप के साथ पहुँचा जाता >&pऔर <&p।
लेकिन में bash, सह प्रक्रिया से पाइप और सह प्रक्रिया करने के लिए अन्य पाइप की फ़ाइल वर्णनकर्ता में लौटा दिया जाता है $COPROCसरणी (क्रमशः ${COPROC[0]}और ${COPROC[1]}। तो ...
सह-प्रक्रिया को डेटा खिलाएं:
echo xxx >&"${COPROC[1]}"
सह प्रक्रिया से डेटा पढ़ें:
read var <&"${COPROC[0]}"
मूल सिंटैक्स के साथ, आप उस समय केवल एक सह-प्रक्रिया शुरू कर सकते हैं।
विस्तारित सिंटैक्स में, आप अपनी सह-प्रक्रियाओं को नाम दे सकते हैं (जैसे कि zshखाली सह-प्रस्ताव में ):
coproc mycoproc { cmd; }
कमांड को कंपाउंड कमांड होना चाहिए। (ध्यान दें कि ऊपर दिया गया उदाहरण कैसे याद दिलाता है function f { ...; })
इस बार, फ़ाइल वर्णनकर्ता में हैं ${mycoproc[0]}और ${mycoproc[1]}।
आप एक समय में एक से अधिक सह-प्रक्रिया शुरू कर सकते हैं - लेकिन आपको एक सह-प्रक्रिया शुरू करते समय एक चेतावनी मिलती है जबकि एक अभी भी चल रही है (गैर-संवादात्मक मोड में भी)।
विस्तारित सिंटैक्स का उपयोग करते समय आप फ़ाइल डिस्क्रिप्टर को बंद कर सकते हैं।
coproc tr { tr a b; }
echo aaa >&"${tr[1]}"
exec {tr[1]}>&-
cat <&"${tr[0]}"
ध्यान दें कि 4.3 के पहले bash संस्करणों में इस तरह से काम करना बंद नहीं होता है, जहां आपको इसके बजाय लिखना होता है:
fd=${tr[1]}
exec {fd}>&-
के रूप में kshऔर zsh, उन पाइप फ़ाइल वर्णनकर्ता पास-ऑन-कार्यकारी के रूप में चिह्नित कर रहे हैं।
लेकिन में bash, निष्पादित आदेशों के लिए उन पारित करने के लिए एक ही रास्ता उन्हें एफडी को नकल करने के लिए है 0, 1या 2। वह सह-प्रक्रियाओं की संख्या को सीमित करता है जिनसे आप एकल कमांड के लिए बातचीत कर सकते हैं। (एक उदाहरण के लिए नीचे देखें।)
yashप्रति सह-फ़ीचर सुविधा नहीं है, लेकिन एक ही अवधारणा को इसकी पाइपलाइन और प्रक्रिया पुनर्निर्देशन सुविधाओं के साथ लागू किया जा सकता है। सिस्टम कॉल के yashलिए एक इंटरफ़ेस है pipe(), इसलिए इस तरह की चीज़ को वहां हाथ से अपेक्षाकृत आसानी से किया जा सकता है।
आप इसके साथ एक सह-प्रक्रिया शुरू करेंगे:
exec 5>>|4 3>(cmd >&5 4<&- 5>&-) 5>&-
जो पहले एक pipe(4,5)(5 लिखने का अंत, 4 पढ़ने का अंत) बनाता है , फिर 3 से पाइप को एक प्रक्रिया में रीडायरेक्ट करता है जो दूसरे छोर पर इसके स्टड के साथ चलता है, और पहले बनाए गए पाइप पर जाने वाला स्टडआउट। फिर हम उस पाइप के लेखन छोर को उस पैरेंट में बंद कर देते हैं जिसकी हमें आवश्यकता नहीं है। तो अब शेल में हमने cmd के स्टैड से जुड़े fd 3 और fd 4 को cmd के स्टैडआउट से पाइप से जोड़ा है।
ध्यान दें कि क्लोज़-ऑन-एक्ज़िक फ़्लैग उन फ़ाइल डिस्क्रिप्टर पर सेट नहीं है।
डेटा खिलाने के लिए:
echo data >&3 4<&-
डेटा पढ़ने के लिए:
read var <&4 3>&-
और आप हमेशा की तरह fds को बंद कर सकते हैं:
exec 3>&- 4<&-
सह-प्रक्रियाओं को मानक नामित पाइपों के साथ आसानी से लागू किया जा सकता है। मुझे नहीं पता कि कब नामांकित पाइप पेश किए गए थे, लेकिन यह संभव है कि kshसह-प्रक्रियाओं के साथ आने के बाद (शायद 80 के दशक के मध्य में, ksh88 को 88 में "रिलीज़" किया गया था, लेकिन मेरा मानना है कि kshकुछ साल पहले एटी एंड टी में आंतरिक रूप से उपयोग किया गया था वह) जो समझाएगा क्यों।
cmd |&
echo data >&p
read var <&p
के साथ लिखा जा सकता है:
mkfifo in out
cmd <in >out &
exec 3> in 4< out
echo data >&3
read var <&4
उन लोगों के साथ बातचीत करना अधिक सरल है - खासकर यदि आपको एक से अधिक सह-प्रक्रिया चलाने की आवश्यकता है। (नीचे दिए गए उदाहरण देखें।)
उपयोग करने coprocका एकमात्र लाभ यह है कि आपको उपयोग के बाद उन नामित पाइपों को साफ करने की आवश्यकता नहीं है।
गोले कुछ निर्माणों में पाइप का उपयोग करते हैं:
cmd1 | cmd2 ,$(cmd) ,<(cmd) , >(cmd)।उन में, डेटा विभिन्न प्रक्रियाओं के बीच केवल एक दिशा में बहता है।
सह-प्रक्रियाओं और नामित पाइपों के साथ, हालांकि, गतिरोध में भागना आसान है। आपको इस बात का ध्यान रखना होगा कि किस कमांड में कौन सी फाइल डिस्क्रिप्टर खुली है, जिससे किसी एक को खुला रहने और किसी प्रक्रिया को जीवित रखने से रोका जा सके। गतिरोध की जांच मुश्किल हो सकती है, क्योंकि वे गैर-नियतात्मक रूप से हो सकते हैं; उदाहरण के लिए, केवल जब एक पाइप को भरने के लिए जितना डेटा भेजा जाता है।
expectजो इसके लिए डिज़ाइन किया गया है, उससे भी बदतर काम करता हैसह-प्रक्रियाओं का मुख्य उद्देश्य शेल को आदेशों के साथ बातचीत करने का एक तरीका प्रदान करना था। हालाँकि, यह इतनी अच्छी तरह से काम नहीं करता है।
ऊपर उल्लिखित गतिरोध का सबसे सरल रूप है:
tr a b |&
echo a >&p
read var<&p
क्योंकि इसका आउटपुट किसी टर्मिनल पर नहीं जाता है, trइसका आउटपुट बफ़र करता है। इसलिए यह तब तक कुछ भी आउटपुट नहीं करेगा, जब तक कि यह इसके बारे में अंत-फ़ाइल नहीं देखता है stdin, या इसने डेटा को आउटपुट के लिए एक बफर-पूर्ण जमा किया है। इसलिए ऊपर, शेल के आउटपुट a\n(केवल 2 बाइट्स) के बाद, readअनिश्चित काल के लिए ब्लॉक हो जाएगा क्योंकि trशेल को और अधिक डेटा भेजने का इंतजार है।
संक्षेप में, कमांड के साथ बातचीत करने के लिए पाइप अच्छे नहीं हैं। सह-प्रक्रियाओं का उपयोग केवल उन कमांडों के साथ बातचीत करने के लिए किया जा सकता है जो अपने आउटपुट को बफर नहीं करते हैं, या जिन कमांड को उनके आउटपुट को बफर नहीं करने के लिए कहा जा सकता है; उदाहरण के लिए, stdbufहाल ही में जीएनयू या फ्रीबीएसडी सिस्टम पर कुछ कमांड का उपयोग करके ।
इसीलिए expectया zptyइसके बजाय छद्म टर्मिनलों का उपयोग करें। expectआदेशों के साथ बातचीत करने के लिए डिज़ाइन किया गया एक उपकरण है, और यह इसे अच्छी तरह से करता है।
सह-प्रक्रियाओं का उपयोग कुछ अधिक जटिल प्लंबिंग करने के लिए किया जा सकता है जो सरल शेल पाइपों की अनुमति देते हैं।
अन्य Unix.SE उत्तर में एक कोप्रोक उपयोग का उदाहरण है।
यहां एक सरल उदाहरण दिया गया है: कल्पना करें कि आप एक ऐसा फंक्शन चाहते हैं, जो कमांड के आउटपुट की एक कॉपी को 3 अन्य कमांड्स को फीड करता हो, और फिर उन 3 कमांड्स के आउटपुट को कॉन्सेट हो जाता है।
सभी पाइप का उपयोग कर।
उदाहरण के लिए: के उत्पादन में फ़ीड printf '%s\n' foo barकरने के लिए tr a b, sed 's/./&&/g'और cut -b2-की तरह कुछ प्राप्त करने के लिए:
foo
bbr
ffoooo
bbaarr
oo
ar
सबसे पहले, यह स्पष्ट रूप से स्पष्ट नहीं है, लेकिन वहां गतिरोध की संभावना है, और यह केवल कुछ किलोबाइट डेटा के बाद होने लगेगा।
फिर, आपके शेल के आधार पर, आप कई अलग-अलग समस्याओं में भाग लेंगे जिन्हें अलग-अलग तरीके से संबोधित किया जाना है।
उदाहरण के लिए, zshआप इसके साथ करेंगे:
f() (
coproc tr a b
exec {o1}<&p {i1}>&p
coproc sed 's/./&&/g' {i1}>&- {o1}<&-
exec {o2}<&p {i2}>&p
coproc cut -c2- {i1}>&- {o1}<&- {i2}>&- {o2}<&-
tee /dev/fd/$i1 /dev/fd/$i2 >&p {o1}<&- {o2}<&- &
exec cat /dev/fd/$o1 /dev/fd/$o2 - <&p {i1}>&- {i2}>&-
)
printf '%s\n' foo bar | f
ऊपर, सह-प्रक्रिया fds में क्लोज़-ऑन-एक्ज़िक फ़्लैग सेट है, लेकिन वे नहीं जो उनसे डुप्लिकेट किए गए हैं (जैसा कि {o1}<&p)। इसलिए, गतिरोधों से बचने के लिए, आपको यह सुनिश्चित करना होगा कि वे किसी भी प्रक्रिया में बंद हैं जिनकी उन्हें आवश्यकता नहीं है।
इसी तरह, हमें exec catअंत में एक सबशेल और उपयोग करना होगा, यह सुनिश्चित करने के लिए कि पाइप खोल के बारे में कोई शेल प्रक्रिया झूठ नहीं है।
kshयहां ( साथ ksh93), जो होना चाहिए:
f() (
tr a b |&
exec {o1}<&p {i1}>&p
sed 's/./&&/g' |&
exec {o2}<&p {i2}>&p
cut -c2- |&
exec {o3}<&p {i3}>&p
eval 'tee "/dev/fd/$i1" "/dev/fd/$i2"' >&"$i3" {i1}>&"$i1" {i2}>&"$i2" &
eval 'exec cat "/dev/fd/$o1" "/dev/fd/$o2" -' <&"$o3" {o1}<&"$o1" {o2}<&"$o2"
)
printf '%s\n' foo bar | f
( नोट: यह उन प्रणालियों पर काम नहीं करेगा जहां लिनक्स के बजाय kshउपयोग करता socketpairsहै pipes, और जहां /dev/fd/nलिनक्स पर काम करता है।)
में ksh, एफडी ऊपर 2करीब-ऑन-कार्यकारी ध्वज के साथ, जब तक वे कमांड लाइन पर स्पष्ट रूप से पारित कर दिया रहे हैं चिह्नित कर रहे हैं। यही कारण है कि हम साथ की तरह अप्रयुक्त फ़ाइल वर्णनकर्ता बंद की जरूरत नहीं है है zsh-लेकिन यह भी वजह है कि हम क्या करना है {i1}>&$i1और उपयोग करने evalकी है कि नए मूल्य के लिए $i1, करने के लिए भेजा जा सके teeऔर cat...
इसमें bashऐसा नहीं किया जा सकता, क्योंकि आप क्लोज़-ऑन-एक्ज़िक फ़्लैग से बच नहीं सकते।
ऊपर, यह अपेक्षाकृत सरल है, क्योंकि हम केवल साधारण बाहरी आदेशों का उपयोग करते हैं। यह तब और अधिक जटिल हो जाता है जब आप इसके बजाय शेल निर्माणों का उपयोग करना चाहते हैं, और आप शेल बग्स में चलना शुरू करते हैं।
नामित पाइपों का उपयोग करके उपरोक्त की तुलना करें:
f() {
mkfifo p{i,o}{1,2,3}
tr a b < pi1 > po1 &
sed 's/./&&/g' < pi2 > po2 &
cut -c2- < pi3 > po3 &
tee pi{1,2} > pi3 &
cat po{1,2,3}
rm -f p{i,o}{1,2,3}
}
printf '%s\n' foo bar | f
आप एक आदेश, उपयोग के साथ बातचीत करना चाहते हैं expect, या zshकी zpty, या नामित पाइप।
यदि आप पाइप के साथ कुछ फैंसी प्लंबिंग करना चाहते हैं, तो नामित पाइप का उपयोग करें।
सह-प्रक्रियाएं उपरोक्त में से कुछ कर सकती हैं, लेकिन कुछ भी गैर-तुच्छ के लिए कुछ गंभीर सिर खरोंच करने के लिए तैयार रहें।
exec {tr[1]}>&-वास्तव में नए संस्करणों के साथ काम करने लगता है और सीडब्ल्यूआरयू / चेंगलॉग प्रविष्टि ( {सरणी [ind]} जैसे शब्दों को मान्य पुनर्निर्देशन ... 2012-09-01) की अनुमति देता है। exec {tr[1]}<&-(या अधिक सही >&-समतुल्य है, हालांकि इससे कोई अंतर नहीं पड़ता है क्योंकि close()दोनों के लिए कॉल केवल ) कोप्रो के स्टड को बंद नहीं करता है, लेकिन पाइप का लेखन अंत उस कोप्रोक को लिखता है।
yash।
mkfifoयह है कि आपको पाइप एक्सेस के लिए दौड़ की स्थिति और सुरक्षा के बारे में चिंता करने की ज़रूरत नहीं है। आपको अभी भी फीफो के साथ गतिरोध के बारे में चिंता करनी होगी।
stdbufकमान उनमें से कम से कम कुछ को रोकने में मदद कर सकती है। मैंने इसे लिनक्स और बैश के तहत इस्तेमाल किया। वैसे भी मेरा मानना है कि @ स्टेफेनचैजेलस निष्कर्ष में सही है: "हेड स्क्रैचिंग" चरण मेरे लिए तभी समाप्त हुआ जब मैं नामांकित पाइप पर वापस लौटा।
सह-प्रक्रियाओं को पहली बार शेल स्क्रिप्टिंग भाषा में ksh88शेल (1988) के साथ शुरू किया गया था, और बाद में zsh1993 से पहले किसी बिंदु पर।
Ksh के तहत सह-प्रक्रिया शुरू करने का सिंटैक्स है command |&। वहां से शुरू करके, आप इसके commandसाथ मानक इनपुट को लिख सकते हैं print -pऔर इसके मानक आउटपुट को पढ़ सकते हैं read -p।
कुछ दशकों बाद, बैश को इस फीचर की कमी खल रही थी, जिसने अंततः इसे 4.0 रिलीज में पेश किया। दुर्भाग्य से, एक असंगत और अधिक जटिल वाक्यविन्यास चुना गया था।
बश 4.0 और नए के तहत, आप coprocकमांड के साथ सह-प्रक्रिया शुरू कर सकते हैं , जैसे:
$ coproc awk '{print $2;fflush();}'
इसके बाद आप कमांड स्टड के लिए कुछ इस तरह से पास कर सकते हैं:
$ echo one two three >&${COPROC[1]}
और इसके साथ awk आउटपुट पढ़ें:
$ read -ru ${COPROC[0]} foo
$ echo $foo
two
Ksh के तहत, यह होगा:
$ awk '{print $2;fflush();}' |&
$ print -p "one two three"
$ read -p foo
$ echo $foo
two
एक "कोप्रोक" क्या है?
यह "सह-प्रक्रिया" के लिए छोटा है जिसका अर्थ है शेल के साथ सहयोग करने वाली दूसरी प्रक्रिया। यह कमांड के अंत में एक "और" के साथ शुरू की गई पृष्ठभूमि की नौकरी के समान है, सिवाय इसके कि इसके मूल शेल के रूप में समान मानक इनपुट और आउटपुट साझा करने के बजाय, इसका मानक I / O एक विशेष द्वारा मूल शेल से जुड़ा हुआ है एक FIFO.For संदर्भ नामक पाइप के प्रकार यहाँ क्लिक करें
एक के साथ zsh में एक coproc शुरू होता है
coproc command
कमांड को स्टडिन से पढ़ने और / या स्टडआउट पर लिखने के लिए तैयार रहना पड़ता है, या यह कॉप्रोक के रूप में ज्यादा उपयोग नहीं होता है।
इस लेख को यहां पढ़ें यह निष्पादन और मैथुन के बीच एक केस स्टडी प्रदान करता है
|। (जो कि अधिकांश गोले में पाइप का उपयोग होता है, और ksh93 में सॉकेटपेयर)। पाइप और सॉकेटपेयर पहले-में, पहले-बाहर, वे सभी फीफो हैं। mkfifoनामित पाइप बनाता है, कोप्रोसेस नाम पाइप का उपयोग नहीं करते हैं।
यहाँ एक और अच्छा (और काम करने वाला) उदाहरण है - BASH में लिखा गया एक सरल सर्वर। कृपया ध्यान दें कि आपको OpenBSD की आवश्यकता होगी netcat, क्लासिक काम नहीं करेगा। बेशक आप यूनिक्स एक के बजाय inet सॉकेट का उपयोग कर सकते हैं।
server.sh:
#!/usr/bin/env bash
SOCKET=server.sock
PIDFILE=server.pid
(
exec </dev/null
exec >/dev/null
exec 2>/dev/null
coproc SERVER {
exec nc -l -k -U $SOCKET
}
echo $SERVER_PID > $PIDFILE
{
while read ; do
echo "pong $REPLY"
done
} <&${SERVER[0]} >&${SERVER[1]}
rm -f $PIDFILE
rm -f $SOCKET
) &
disown $!
client.sh:
#!/usr/bin/env bash
SOCKET=server.sock
coproc CLIENT {
exec nc -U $SOCKET
}
{
echo "$@"
read
} <&${CLIENT[0]} >&${CLIENT[1]}
echo $REPLY
उपयोग:
$ ./server.sh
$ ./client.sh ping
pong ping
$ ./client.sh 12345
pong 12345
$ kill $(cat server.pid)
$
bash 4.3.11, अब आप एक आक्स की आवश्यकता के बिना सीधे कॉपीक्रॉप फ़ाइल डिस्क्रिप्टर को बंद कर सकते हैं। चर; अपने जवाब में उदाहरण के मामले मेंexec {tr[1]}<&-अब काम करेगा (coproc के stdin बंद करने के लिए, ध्यान दें कि आपके कोड (परोक्ष रूप से) को बंद करने की कोशिश करता है{tr[1]}का उपयोग करते हुए>&-, लेकिन{tr[1]}coproc की है stdin , और साथ बंद होना चाहिए<&-)। ठीक बीच में कहीं आ गया होगा4.2.25, जो अभी भी समस्या को प्रदर्शित करता है, और4.3.11, जो नहीं करता है।