দুটি প্রোগ্রামের মধ্যে কীভাবে দ্বিদলীয় পাইপ তৈরি করবেন?


63

সবাই কিভাবে দুই প্রোগ্রামের মধ্যে (বাঁধাই করা একমুখী নল করতে জানে stdoutপ্রথমটি ও stdinদ্বিতীয় এক): first | second

তবে কীভাবে দ্বিপাক্ষিক পাইপ তৈরি করা যায়, অর্থাৎ ক্রস-বাইন্ড stdinএবং stdoutদুটি প্রোগ্রাম? শেল এটি করার সহজ উপায় আছে?

shell  pipe 

উত্তর:


30

যদি আপনার সিস্টেমে পাইপগুলি দ্বি নির্দেশমূলক হয় (সেগুলি সোলারিস 11 এবং কিছু বিএসডি অন্তত তবে লিনাক্স নয়):

cmd1 <&1 | cmd2 >&0

যদিও অচলাবস্থা থেকে সাবধান।

এছাড়াও লক্ষ করুন যে কিছু সিস্টেমে ksh93 এর কয়েকটি সংস্করণ সকেট জুড়ি| ব্যবহার করে পাইপ ( ) প্রয়োগ করে । সকেট জোড় দ্বি নির্দেশমূলক, তবে ksh93 স্পষ্টত বিপরীত দিকটি বন্ধ করে দেয়, সুতরাং উপরের কমান্ডটি সেই সিস্টেমে যেখানে পাইপগুলি ( সিস্টেম কল দ্বারা তৈরি করা হয়েছে ) দ্বিদ্বিদ্বন্দ্বী সেখানে সেই ksh93 গুলি ব্যবহার করে না ।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
@ আন্ড্রেভিহারভ এটি সত্য, আপনি নাম দেওয়া ব্যক্তির মধ্যে একটির জন্য বেনামে পাইপ স্থাপন করতে পারেন। তবে আমি প্রতিসাম্যটি পছন্দ করি :
পি

3
@ ব্যবহারকারী ১৪৪৪৪: লিনাক্সে, আপনি সম্ভবত এটির মতো কিছু করতে পারেন prog1 < fifo | tee /dev/stderr | prog2 | tee /dev/stderr > fifo
আন্দ্রে ভিহারভ

3
আপনি এটি করতে পারেন prog2 < fifo0 > fifo1হবে তখন আপনি আপনার সামান্য নাচ এড়াতে পারেন exec 30< ...(যা প্রণালী দ্বারা শুধুমাত্র সঙ্গে কাজ করে bashবা yashযে মত 10 fds জন্য)।
স্টাফেন চেজেলাস

1
@ জাস্ট হুমম, আপনি ঠিক বলেছেন যে কোনও প্রয়োজন নেই, কমপক্ষে ব্যাশে b আমি সম্ভবত চিন্তিত ছিলাম যেহেতু শেলটি পুনর্নির্দেশগুলি সম্পাদন করে (পাইপগুলি খোলার সহ), এটি ব্লক করে — তবে ফিফোস খোলার আগে কমপক্ষে ব্যাশ কাঁটাচামচ। dashখুব ঠিক আছে বলে মনে হয় (তবে কিছুটা আলাদা আচরণ করে)
ডারউবার্ট

13

আপনি যা করার চেষ্টা করছেন এটি এটি কিনা আমি নিশ্চিত নই:

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

এটি 8096 পোর্টে একটি শ্রবণকারী সকেট খোলার মাধ্যমে শুরু হয় এবং একবার সংযোগ স্থাপন হয়ে গেলে এর সাথে স্ট্রিম আউটপুট এবং স্ট্রিম ইনপুট হিসাবে প্রোগ্রাম তৈরি secondহয় ।stdinstdout

তারপরে, একটি সেকেন্ড ncচালু করা হয়েছে যা শ্রবণ পোর্টের সাথে সংযোগ স্থাপন করে এবং প্রবাহের ইনপুট হিসাবে এবং স্ট্রিম আউটপুট হিসাবে এর firstসাথে প্রোগ্রাম তৈরি করে ।stdoutstdin

এটি কোনও পাইপ ব্যবহার করে ঠিক করা হয় নি তবে এটি আপনার যা প্রয়োজন তা করা মনে হচ্ছে।

এটি যেমন নেটওয়ার্কটি ব্যবহার করে, এটি 2 টি দূরবর্তী কম্পিউটারে করা যেতে পারে। ওয়েব সার্ভার ( second) এবং একটি ওয়েব ব্রাউজার ( first) কাজ করার প্রায় এটিই ।


1
এবং nc -Uইউনিক্স ডোমেন সকেটের জন্য যা কেবল ফাইল সিস্টেমের ঠিকানা স্থান নেয় space
সিরো সান্তিলি 新疆 改造 中心 法轮功 六四

লিনাক্স -c বিকল্প উপলব্ধ নেই। আমার সুখ স্বল্প সময়ের জন্য ছিল :-(
পাবলোচাকিন


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 &&     # 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
}

নামকরণ করা পাইপটি আইওলাপ সেটআপের সময় কেবলমাত্র ফাইল সিস্টেমে উপস্থিত রয়েছে। এই ফাংশনটি বেশ পসিক্স নয় কারণ এমকেটেম্প অবমূল্যায়ন করা হয়েছে (এবং কোনও রেস আক্রমণের পক্ষে সম্ভাব্য ঝুঁকিপূর্ণ)।

/ প্রোক / ব্যবহার করে একটি লিনাক্স-নির্দিষ্ট প্রয়োগ সম্ভব হয় যার জন্য নামযুক্ত পাইপের প্রয়োজন হয় না তবে আমি মনে করি এটি যথেষ্ট ভালের চেয়ে বেশি।


আকর্ষণীয় ফাংশন, +1। সম্ভবত একটি বাক্য ব্যবহার করতে পারেন বা 2 ( : <$FIFO & )আরও বিশদে ব্যাখ্যা করে যুক্ত করা হয়েছে । পোস্ট করার জন্য আপনাকে ধন্যবাদ।
অ্যালেক্স স্ট্রিজি

আমি কিছুটা আশেপাশে তাকিয়ে খালি উঠে এলাম; এর অবচয় সম্পর্কে আমি কোথায় তথ্য জানতে পারি mktemp? আমি এটি ব্যাপকভাবে ব্যবহার করি এবং যদি কোনও নতুন সরঞ্জাম এটির জায়গা করে নিয়ে থাকে তবে আমি এটি ব্যবহার শুরু করতে চাই।
ডোপঘোটি

অ্যালেক্স: একটি ন্যানড পাইপের ওপেন (2) সিস্কেল ব্লক করছে। আপনি যদি "এক্সিকিউটিভ <IP পাইপ> $ পাইপ" চেষ্টা করেন তবে অন্য প্রক্রিয়াটি অন্যদিকে খোলার অপেক্ষায় আটকে যাবে। ": <$ FIFO &" কমান্ডটি ব্যাকগ্রাউন্ডের একটি সাব-হিলে কার্যকর করা হয়েছে এবং দ্বি-দিকনির্দেশনা পুনঃনির্দেশকে সাফল্যের সাথে শেষ করতে সক্ষম করে।
ব্যবহারকারী 873942

ডোপঘোটি: এমকেটেম্প (3) সি লাইব্রেরির ফাংশন অবচিত করা হয়েছে। Mktemp (1) ইউটিলিটিটি নয়।
ব্যবহারকারী 873942

4

এছাড়াও আছে

যেহেতু @ স্টাফেনচাজেলা মন্তব্যগুলিতে সঠিকভাবে নোট করেছেন, উপরের উদাহরণগুলি "বেস ফর্ম", অনুরূপ প্রশ্নের উত্তর দেওয়ার জন্য তার বিকল্পগুলির সাথে চমৎকার উদাহরণ রয়েছে


নোট করুন যে ডিফল্টরূপে socatপাইপের পরিবর্তে সকেট ব্যবহার করে (আপনি এটি দিয়ে পরিবর্তন করতে পারেন commtype=pipes)। আপনি noforkপাইপ / সকেটের মধ্যে অতিরিক্ত সোকা প্রসেসটি মুভিং ডেটা এড়াতে বিকল্পটি যুক্ত করতে চাইতে পারেন । ( আমার উত্তর বিটিডব্লিউতে সম্পাদনার জন্য ধন্যবাদ )
স্টাফেন চেজেলাস

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
...
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.