दो कार्यक्रमों के बीच द्विदिश पाइप कैसे बनायें?


63

हर कोई जानता है कि दो कार्यक्रमों के बीच यूनिडायरेक्शनल पाइप कैसे बनाया जाता है ( stdoutपहले एक का और stdinदूसरे का बंधन ) first | second:।

लेकिन द्विदिश पाइप, यानी क्रॉस-बाइंड stdinऔर stdoutदो प्रोग्राम कैसे बनाएं ? क्या इसे शेल में करने का आसान तरीका है?

shell  pipe 

जवाबों:


30

यदि आपके सिस्टम पर पाइप द्विदिश हैं (जैसा कि वे सोलारिस 11 और कुछ बीएसडी पर कम से कम हैं, लेकिन लिनक्स नहीं):

cmd1 <&1 | cmd2 >&0

गतिरोधों से सावधान रहें।

यह भी ध्यान दें कि कुछ प्रणालियों पर ksh93 के कुछ संस्करण |एक सॉकेट जोड़ी का उपयोग करके पाइप ( ) को लागू करते हैं । सॉकेट जोड़े द्विदिश हैं, लेकिन ksh93 स्पष्ट रूप से रिवर्स दिशा को बंद कर देता है, इसलिए उपरोक्त कमांड उन ksh93s के साथ सिस्टम पर भी काम नहीं करेगा जहां पाइप ( pipe(2)सिस्टम कॉल द्वारा बनाई गई ) द्विदिश हैं।


1
किसी को पता है कि क्या यह भी लिनक्स पर काम करता है? (यहाँ archlinux का प्रयोग करके)
heinrich5991


41

खैर, नामित पाइप ( mkfifo) के साथ इसकी काफी "आसान" है । मैंने उद्धरणों में आसान रखा क्योंकि जब तक इस के लिए कार्यक्रम तैयार नहीं किए जाते, गतिरोध की संभावना है।

mkfifo fifo0 fifo1
( prog1 > fifo0 < fifo1 ) &
( prog2 > fifo1 < fifo0 ) &
( exec 30<fifo0 31<fifo1 )      # write can't open until there is a reader
                                # and vice versa if we did it the other way

अब, आम तौर पर लेखन को शामिल करने में बफरिंग होती है। इसलिए, उदाहरण के लिए, यदि दोनों कार्यक्रम थे:

#!/usr/bin/perl
use 5.010;
say 1;
print while (<>);

आप एक अनंत लूप की उम्मीद करेंगे। लेकिन इसके बजाय, दोनों गतिरोध करेंगे; $| = 1आउटपुट बफ़रिंग को बंद करने के लिए आपको (या समतुल्य) जोड़ना होगा । गतिरोध इसलिए होता है क्योंकि दोनों प्रोग्राम स्टड पर किसी चीज़ की प्रतीक्षा कर रहे हैं, लेकिन वे इसे नहीं देख रहे हैं क्योंकि इसके दूसरे प्रोग्राम के स्टडआउट बफर में बैठे हैं, और अभी तक पाइप को नहीं लिखा गया है।

अपडेट : स्टीफन चारजेलस और जोस्ट के सुझावों को शामिल करना:

mkfifo fifo0 fifo1
prog1 > fifo0 < fifo1 &
prog2 < fifo0 > fifo1

वही करता है, छोटा है, और अधिक पोर्टेबल है।


23
एक का नाम पाइप के लिए पर्याप्त है: prog1 < fifo | prog2 > fifo
एंड्रे विह्रोव

2
@AndreyVihrov यह सच है, आप किसी नामांकित व्यक्ति के लिए एक अनाम पाइप स्थानापन्न कर सकते हैं। लेकिन मुझे समरूपता पसंद है :-P
derobert

3
@ user14284: लिनक्स पर, आप शायद इसे कुछ इस तरह से कर सकते हैं prog1 < fifo | tee /dev/stderr | prog2 | tee /dev/stderr > fifo
एंड्री विह्रोव

3
यदि आप इसे बनाते हैं prog2 < fifo0 > fifo1, तो आप अपने छोटे से डांस से बच सकते हैं exec 30< ...(जो कि केवल 10 से अधिक के साथ काम करता है bashया इस तरह yashसे fds के लिए)।
स्टीफन चेजलस

1
@ जोस्ट हम्म, प्रतीत होता है कि आप सही हैं कि कोई आवश्यकता नहीं है, कम से कम बैश में। मुझे शायद इस बात की चिंता थी कि चूंकि शेल पुनर्निर्देशन करता है (पाइप खोलने सहित), यह ब्लॉक हो सकता है - लेकिन पंद्रह को खोलने से पहले कम से कम बैश कांटे। dashलगता है ठीक भी है (लेकिन थोड़ा अलग तरीके से बर्ताव करता है)
derobert

13

मुझे यकीन नहीं है कि यह वही है जो आप करने की कोशिश कर रहे हैं:

nc -l -p 8096 -c second &
nc -c first 127.0.0.1 8096 &

यह पोर्ट 8096 पर एक सुनने वाला सॉकेट खोलने से शुरू होता है, और एक बार कनेक्शन स्थापित होने के बाद, स्ट्रीम आउटपुट के रूप में और स्ट्रीम इनपुट के रूप में secondइसके साथ प्रोग्राम बनाता है ।stdinstdout

फिर, एक दूसरे ncका शुभारंभ किया जो सुन बंदरगाह को जोड़ता है और कार्यक्रम spawns है firstके साथ अपने stdoutधारा इनपुट और उसके रूप में stdinउत्पादन धारा के रूप में।

यह बिल्कुल एक पाइप का उपयोग करके नहीं किया जाता है, लेकिन ऐसा लगता है कि आपको क्या चाहिए।

जैसा कि यह नेटवर्क का उपयोग करता है, यह 2 दूरस्थ कंप्यूटरों पर किया जा सकता है। यह लगभग एक वेब सर्वर ( second) और एक वेब ब्राउज़र ( first) काम करने का तरीका है ।


1
और nc -UUNIX डोमेन सॉकेट्स के लिए जो केवल फाइलसिस्टम एड्रेस स्पेस लेते हैं।
सिरो सेंटिल्ली 新疆 改造 iro i

लिनक्स में -c विकल्प उपलब्ध नहीं है। मेरी खुशी एक छोटा समय था:
pablochacin


6

bashसंस्करण 4 में coprocकमांड है जो इसे bashबिना नाम के पाइप में शुद्ध करने की अनुमति देता है :

coproc cmd1
eval "exec cmd2 <&${COPROC[0]} >&${COPROC[1]}"

कुछ अन्य गोले भी कर सकते हैं coproc

नीचे अधिक विस्तृत उत्तर दिया गया है, लेकिन दो के बजाय तीन आदेशों को जंजीर दिया गया है, जो केवल एक छोटे से अधिक दिलचस्प बनाता है।

यदि आप भी उपयोग करने के लिए खुश हैं catऔर stdbufफिर निर्माण को समझने में आसान बनाया जा सकता है।

संस्करण का उपयोग bashकर catऔर stdbuf, समझने में आसान:

# start pipeline
coproc {
    cmd1 | cmd2 | cmd3
}
# create command to reconnect STDOUT `cmd3` to STDIN of `cmd1`
endcmd="exec stdbuf -i0 -o0 /bin/cat <&${COPROC[0]} >&${COPROC[1]}"
# eval the command.
eval "${endcmd}"

ध्यान दें, eval का उपयोग करना है क्योंकि <और $ var में चर का विस्तार मेरे बैश 4.2.25 के छंद में अवैध है।

शुद्ध का उपयोग करते हुए संस्करण bash: दो भागों में टूटना, पहली पाइप लाइन को कॉप्रोक के नीचे लॉन्च करना, फिर दोपहर के भोजन के दूसरे भाग (या तो एक कमांड या एक पाइपलाइन) को पहले में फिर से जोड़ना:

coproc {
    cmd 1 | cmd2
}
endcmd="exec cmd3 <&${COPROC[0]} >&${COPROC[1]}"
eval "${endcmd}"

अवधारणा के सुबूत:

फ़ाइल ./prog, उपभोग करने के लिए सिर्फ एक डमी प्रोग, टैग और री-प्रिंट लाइनें। ओवरकलिंग से होने वाली समस्याओं से बचने के लिए सब-हेल्प का उपयोग करना, यहाँ बात नहीं है।

#!/bin/bash
let c=0
sleep 2

[ "$1" == "1" ] && ( echo start )

while : ; do
  line=$( head -1 )
  echo "$1:${c} ${line}" 1>&2
  sleep 2
  ( echo "$1:${c} ${line}" )
  let c++
  [ $c -eq 3 ] && exit
done

फ़ाइल ./start_cat यह एक संस्करण का उपयोग कर रहा है bash, catऔरstdbuf

#!/bin/bash

echo starting first cmd>&2

coproc {
  stdbuf -i0 -o0 ./prog 1 \
    | stdbuf -i0 -o0 ./prog 2 \
    | stdbuf -i0 -o0 ./prog 3
}

echo "Delaying remainer" 1>&2
sleep 5
cmd="exec stdbuf -i0 -o0 /bin/cat <&${COPROC[0]} >&${COPROC[1]}"

echo "Running: ${cmd}" >&2
eval "${cmd}"

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

#!/bin/bash

echo starting first cmd>&2

coproc {
  stdbuf -i0 -o0 ./prog 1 \
    | stdbuf -i0 -o0 ./prog 2
}

echo "Delaying remainer" 1>&2
sleep 5
cmd="exec stdbuf -i0 -o0 ./prog 3 <&${COPROC[0]} >&${COPROC[1]}"

echo "Running: ${cmd}" >&2
eval "${cmd}"

आउटपुट:

> ~/iolooptest$ ./start_part
starting first cmd
Delaying remainer
2:0 start
Running: exec stdbuf -i0 -o0 ./prog 3 <&63 >&60
3:0 2:0 start
1:0 3:0 2:0 start
2:1 1:0 3:0 2:0 start
3:1 2:1 1:0 3:0 2:0 start
1:1 3:1 2:1 1:0 3:0 2:0 start
2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
3:2 2:2 1:1 3:1 2:1 1:0 3:0 2:0 start
1:2 3:2 2:2 1:1 3:1 2:1 1:0 3:0 2:0 start

उसने ऐसा किया।


5

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

ioloop &&     # stdout -> stdin 
cmd1 | cmd2   # stdin -> cmd1 -> cmd2 -> stdout (-> back to stdin)

यदि आप शीर्ष-स्तरीय शेल के डिस्क्रिप्टर को संशोधित नहीं करना चाहते हैं, तो इसे सब-शेल में चलाएं:

( ioloop && cmd1 | cmd2 )

यहाँ एक नामित पाइप का उपयोग करके आयोलोप का एक पोर्टेबल कार्यान्वयन है:

ioloop() {
    FIFO=$(mktemp -u /tmp/ioloop_$$_XXXXXX ) &&
    trap "rm -f $FIFO" EXIT &&
    mkfifo $FIFO &&
    ( : <$FIFO & ) &&    # avoid deadlock on opening pipe
    exec >$FIFO <$FIFO
}

नामांकित पाइप ioloop सेटअप के दौरान केवल संक्षेप में फाइल सिस्टम में मौजूद है। यह फ़ंक्शन काफी पोसिक्स नहीं है क्योंकि मटमैप को पदावनत किया जाता है (और संभवतः एक दौड़ हमले के लिए असुरक्षित)।

/ Proc / का उपयोग करके एक linux-specific कार्यान्वयन संभव है जिसे नामांकित पाइप की आवश्यकता नहीं है, लेकिन मुझे लगता है कि यह एक से अधिक अच्छा है।


दिलचस्प समारोह, +1। शायद एक वाक्य का उपयोग कर सकता है या 2 को ( : <$FIFO & )और अधिक विस्तार से समझा सकता है। प्रविष्टि के लिए धन्यवाद।
एलेक्स स्ट्रैगीज

मैंने थोड़ा इधर-उधर देखा और खाली आया; मुझे पदावनति के बारे में जानकारी कहां मिल सकती है mktemp? मैं इसे बड़े पैमाने पर उपयोग करता हूं, और अगर एक नए उपकरण ने इसकी जगह ले ली है, तो मैं इसका उपयोग करना शुरू करना चाहता हूं।
डोपघोटी

एलेक्स: एक नेड पाइप पर खुला (2) syscall अवरुद्ध है। यदि आप "$ PIPE> $ PIPE" निष्पादित करने का प्रयास करते हैं, तो यह दूसरी प्रक्रिया के दूसरी ओर खुलने की प्रतीक्षा में अटक जाएगा। कमांड ": <$ FIFO &" पृष्ठभूमि में एक उपधारा में निष्पादित होता है और सफलतापूर्वक पूरा करने के लिए द्विदिश पुनर्निर्देशन को सक्षम करता है।
user873942

DopeGhoti: mktemp (3) C लाइब्रेरी फ़ंक्शन को पदावनत किया गया है। Mktemp (1) उपयोगिता नहीं है।
user873942

4

वहाँ भी

जैसा कि @ StéphaneChazelas ने टिप्पणियों में सही ढंग से नोट किया है, ऊपर दिए गए उदाहरण "आधार रूप" हैं, उनके पास एक समान प्रश्न के लिए उनके उत्तर के विकल्पों के साथ अच्छे उदाहरण हैं


ध्यान दें कि डिफ़ॉल्ट रूप से, socatपाइप के बजाय सॉकेट का उपयोग करता है (आप इसे बदल सकते हैं commtype=pipes)। आप noforkपाइप / सॉकेट के बीच डेटा की अतिरिक्त सोसाइट प्रक्रिया से बचने के लिए विकल्प जोड़ना चाह सकते हैं । ( मेरे उत्तर btw पर संपादित करने के लिए धन्यवाद )
स्टीफन चेज़लस

0

यहां कई शानदार जवाब हैं। इसलिए मैं उनके साथ आसानी से खेलने के लिए कुछ जोड़ना चाहता हूं। मुझे लगता stderrहै कि कहीं भी पुनर्निर्देशित नहीं किया गया है। दो स्क्रिप्ट बनाएं (a.sh और b.sh को बताएं):

#!/bin/bash
echo "foo" # change to 'bar' in second file

for i in {1..10}; do
  read input
  echo ${input}
  echo ${i} ${0} got: ${input} >&2
done

फिर जब आप उन्हें किसी अच्छे तरीके से जोड़ते हैं, जिसे आपको कंसोल पर देखना चाहिए:

1 ./a.sh got: bar
1 ./b.sh got: foo
2 ./a.sh got: foo
2 ./b.sh got: bar
3 ./a.sh got: bar
3 ./b.sh got: foo
4 ./a.sh got: foo
4 ./b.sh got: bar
...
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.