2008-09-02 14 views
19

में पाइप लूप कैसे बनाएं, मान लें कि मेरे पास P0, P1, P(n-1) कुछ n > 0 के लिए प्रोग्राम हैं। मैं सभी i (0 <= i < n) के लिए प्रोग्राम पर प्रोग्राम Pi के आउटपुट को आसानी से रीडायरेक्ट कैसे कर सकता हूं?बैश

उदाहरण के लिए, मान लीजिए कि मैं एक कार्यक्रम square है, जो बार-बार एक नंबर पढ़ता है और जाने से उस नंबर के वर्ग, और एक कार्यक्रम calc, जो कभी कभी एक नंबर जिसके बाद यह वर्ग पढ़ने में सक्षम हो जाने की उम्मीद प्रिंट प्रिंट इसका मैं इन प्रोग्रामों को कैसे कनेक्ट करूं कि जब भी calc कोई संख्या प्रिंट करता है, square वर्ग यह calc पर लौटाता है?

संपादित करें: मुझे शायद "आसानी से" के साथ मेरा अर्थ स्पष्ट करना चाहिए। नामित पाइप/फीफो समाधान वास्तव में काम करता है (और मैंने अतीत में उपयोग किया है), लेकिन अगर आप इसे बैश पाइप का उपयोग करने के साथ तुलना करते हैं तो इसे ठीक से करने के लिए काफी काम की आवश्यकता होती है। (आपको अभी तक मौजूदा फ़ाइल नाम प्राप्त करने की आवश्यकता नहीं है, उस नाम के साथ एक पाइप बनाएं, "पाइप लूप" चलाएं, नामित पाइप को साफ करें।) कल्पना कीजिए कि आप अब prog1 | prog2 लिख सकते हैं और प्रोग्राम को जोड़ने के लिए हमेशा नामित पाइप का उपयोग करना होगा ।

मैं कुछ ऐसा ढूंढ रहा हूं जो "सामान्य" पाइप लिखने जितना आसान हो। उदाहरण के लिए { prog1 | prog2 } >&0 कुछ अच्छा होगा।

उत्तर

5

यह एक बहुत ही रोचक सवाल है। मैं (अस्पष्ट) 17 साल पहले कॉलेज में बहुत ही असाइनमेंट याद करता हूं। हमें पाइप की एक सरणी बनाना पड़ा, जहां हमारे कोड को प्रत्येक पाइप के इनपुट/आउटपुट के लिए फाइलहेडल मिलेंगे। फिर कोड फोर्क होगा और अप्रयुक्त फ़ाइल हैंडल बंद करें।

मुझे लगता है कि आप बैश में नामित पाइप के साथ कुछ ऐसा कर सकते हैं। अद्वितीय नामों के साथ पाइप का एक सेट बनाने के लिए mknod या mkfifo का उपयोग करें जिसे आप संदर्भित कर सकते हैं, फिर अपने प्रोग्राम को फोर्क करें।

-1

मुझे संदेह है कि sh/bash इसे कर सकता है। जेडएसएच इसके मल्टीटोस और कॉप्रोक सुविधाओं के साथ बेहतर शर्त होगी।

+1

क्या आप ज़श के बारे में एक उदाहरण दे सकते हैं? मुझे इसमें रूचि है। –

1

नामित पाइप।

FIFOs की एक श्रृंखला बनाने के लिए, का उपयोग कर mkfifo

यानी fifo0, fifo1

फिर पाइप आप चाहते करने के लिए समय में प्रत्येक प्रक्रिया देते हैं:

processn < फीफो (n-1)> fifon

13

एक नामित पाइप यह कर सकता है:

$ mkfifo outside 
$ <outside calc | square >outside & 
$ echo "1" >outside ## Trigger the loop to start 
+0

क्या आप " square> के बाहर" पंक्ति को समझा सकते हैं? मैं बाहर <बाहर और> के बारे में अनिश्चित हूँ। –

+0

वे मानक खोल रीडायरेक्ट हैं - 'बाहर' से पढ़ रहे हैं और 'बाहर' आउटपुट कर रहे हैं। बाहर एक फीफो है, इसलिए इसे लिखे गए सब कुछ, पढ़ने के पक्ष में आता है। –

+0

मैंने इस कोड को आजमाया, लेकिन यह काम नहीं करता है। ऐसा लगता है कि यह पंक्ति: '<बाहर कैल्क | वर्ग> बाहर और 'तुरंत समाप्त होता है। – RnMss

24

कल कुछ समय बिताने के बाद stdoutstdin पर रीडायरेक्ट करने का प्रयास कर रहा है, मैं निम्नलिखित विधि के साथ समाप्त हुआ। यह वास्तव में अच्छा नहीं है, लेकिन मुझे लगता है कि मैं इसे नामित पाइप/फीफो समाधान पर पसंद करता हूं।

read | { P0 | ... | P(n-1); } >/dev/fd/0 

{ ... } >/dev/fd/0 stdout अनुप्रेषित करने के लिए एक पूरे के रूप में पाइप दृश्य के लिए stdin करना है (यानी यह P0 के इनपुट के लिए पी (n-1) के उत्पादन में रीडायरेक्ट)। >&0 का उपयोग करना या ऐसा कुछ काम नहीं करता है; यह शायद इसलिए है क्योंकि बैश मानता है 0 केवल पढ़ने के लिए है जबकि इसे /dev/fd/0 पर लिखना कोई फर्क नहीं पड़ता है।

प्रारंभिक read -पइप आवश्यक है क्योंकि इसके बिना इनपुट और आउटपुट फ़ाइल डिस्क्रिप्टर एक ही पीटीएस डिवाइस (कम से कम मेरे सिस्टम पर) हैं और रीडायरेक्ट का कोई प्रभाव नहीं पड़ता है। (पीटीएस डिवाइस पाइप के रूप में काम नहीं करता है; इसके लिए लिखना आपकी स्क्रीन पर चीजें रखता है।) { ... } इनपुट को सामान्य पाइप बनाकर, रीडायरेक्ट का वांछित प्रभाव होता है।

मेरी calc/square उदाहरण के साथ उदाहरण देकर स्पष्ट करने के लिए:

function calc() { 
    # calculate sum of squares of numbers 0,..,10 

    sum=0 
    for ((i=0; i<10; i++)); do 
    echo $i     # "request" the square of i 

    read ii     # read the square of i 
    echo "got $ii" >&2   # debug message 

    let sum=$sum+$ii 
    done 

    echo "sum $sum" >&2   # output result to stderr 
} 

function square() { 
    # square numbers 

    read j       # receive first "request" 
    while [ "$j" != "" ]; do 
    let jj=$j*$j 
    echo "square($j) = $jj" >&2 # debug message 

    echo $jj      # send square 

    read j      # receive next "request" 
    done 
} 

read | { calc | square; } >/dev/fd/0 

ऊपर कोड निम्न उत्पादन देता चल रहा है:

square(0) = 0 
got 0 
square(1) = 1 
got 1 
square(2) = 4 
got 4 
square(3) = 9 
got 9 
square(4) = 16 
got 16 
square(5) = 25 
got 25 
square(6) = 36 
got 36 
square(7) = 49 
got 49 
square(8) = 64 
got 64 
square(9) = 81 
got 81 
sum 285 
बेशक

, इस पद्धति काफी हैक का एक सा है। खासकर read भाग में एक अवांछित साइड-इफेक्ट है: "वास्तविक" पाइप लूप को समाप्त करने से पूरे की समाप्ति नहीं होती है। मैं read से बेहतर कुछ भी नहीं सोच सकता था क्योंकि ऐसा लगता है कि आप केवल यह निर्धारित कर सकते हैं कि पाइप लूप को कुछ लिखने की कोशिश करके समाप्त कर दिया गया है।

+1

अच्छा समाधान। मुझे लूप के अंदर नेटकैट का उपयोग करके कुछ ऐसा करना पड़ा और 'इको' के साथ अपने इनपुट को 'बंद' करके 'रीड' साइड इफेक्ट के आसपास काम किया। अंत में यह कुछ ऐसा था: echo | पढ़ें | {पी 0 | ... | पी (n-1); }>/dev/fd/0 –

+0

'echo | read' के बजाय कोई भी आदेश को तुरंत समाप्त कर सकता है, जैसे ':' ('true'), उदा। ': | {cmd | cmd>/dev/fd/0} '।उदाहरण: ': | {एनसी-एलपी 5000>/dev/fd/0; } 'एक साधारण गूंज सर्वर है जो क्लाइंट ईओएफ पर सही ढंग से समाप्त हो जाता है। – regnarg

-2

एक कमांड स्टैक को मनमाने ढंग से कमांड की सरणी से स्ट्रिंग के रूप में बनाया जा सकता है और eval के साथ मूल्यांकन किया जा सकता है। निम्न उदाहरण परिणाम 65536.

function square() 
{ 
    read n 
    echo $((n*n)) 
} # ---------- end of function square ---------- 

declare -a commands=('echo 4' 'square' 'square' 'square') 

#------------------------------------------------------------------------------- 
# build the command stack using pipes 
#------------------------------------------------------------------------------- 
declare  stack=${commands[0]} 

for ((COUNTER=1; COUNTER<${#commands[@]}; COUNTER++)); do 
    stack="${stack} | ${commands[${COUNTER}]}" 
done 

#------------------------------------------------------------------------------- 
# run the command stack 
#------------------------------------------------------------------------------- 
eval "$stack" 
+1

मुझे नहीं लगता कि आप इस सवाल का जवाब दे रहे हैं। – reinierpost

3

मेरे समाधान का उपयोग करता है देता है pipexec (समारोह कार्यान्वयन के अधिकांश अपने जवाब से आता है):

square.sh

function square() { 
    # square numbers 

    read j       # receive first "request" 
    while [ "$j" != "" ]; do 
    let jj=$j*$j 
    echo "square($j) = $jj" >&2 # debug message 

    echo $jj      # send square 

    read j      # receive next "request" 
    done 
} 

square [email protected] 

calc.sh

function calc() { 
    # calculate sum of squares of numbers 0,..,10 

    sum=0 
    for ((i=0; i<10; i++)); do 
    echo $i     # "request" the square of i 

    read ii     # read the square of i 
    echo "got $ii" >&2   # debug message 

    let sum=$sum+$ii 
done 

echo "sum $sum" >&2   # output result to stderr 
} 

calc [email protected] 

कमांड

pipexec [ CALC /bin/bash calc.sh ] [ SQUARE /bin/bash square.sh ] \ 
    "{CALC:1>SQUARE:0}" "{SQUARE:1>CALC:0}" 

निर्गम (अपने जवाब में के रूप में ही)

square(0) = 0 
got 0 
square(1) = 1 
got 1 
square(2) = 4 
got 4 
square(3) = 9 
got 9 
square(4) = 16 
got 16 
square(5) = 25 
got 25 
square(6) = 36 
got 36 
square(7) = 49 
got 49 
square(8) = 64 
got 64 
square(9) = 81 
got 81 
sum 285 

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