সবাই কিভাবে দুই প্রোগ্রামের মধ্যে (বাঁধাই করা একমুখী নল করতে জানে stdoutপ্রথমটি ও stdinদ্বিতীয় এক): first | second।
তবে কীভাবে দ্বিপাক্ষিক পাইপ তৈরি করা যায়, অর্থাৎ ক্রস-বাইন্ড stdinএবং stdoutদুটি প্রোগ্রাম? শেল এটি করার সহজ উপায় আছে?
সবাই কিভাবে দুই প্রোগ্রামের মধ্যে (বাঁধাই করা একমুখী নল করতে জানে stdoutপ্রথমটি ও stdinদ্বিতীয় এক): first | second।
তবে কীভাবে দ্বিপাক্ষিক পাইপ তৈরি করা যায়, অর্থাৎ ক্রস-বাইন্ড stdinএবং stdoutদুটি প্রোগ্রাম? শেল এটি করার সহজ উপায় আছে?
উত্তর:
যদি আপনার সিস্টেমে পাইপগুলি দ্বি নির্দেশমূলক হয় (সেগুলি সোলারিস 11 এবং কিছু বিএসডি অন্তত তবে লিনাক্স নয়):
cmd1 <&1 | cmd2 >&0
যদিও অচলাবস্থা থেকে সাবধান।
এছাড়াও লক্ষ করুন যে কিছু সিস্টেমে ksh93 এর কয়েকটি সংস্করণ সকেট জুড়ি| ব্যবহার করে পাইপ ( ) প্রয়োগ করে । সকেট জোড় দ্বি নির্দেশমূলক, তবে ksh93 স্পষ্টত বিপরীত দিকটি বন্ধ করে দেয়, সুতরাং উপরের কমান্ডটি সেই সিস্টেমে যেখানে পাইপগুলি ( সিস্টেম কল দ্বারা তৈরি করা হয়েছে ) দ্বিদ্বিদ্বন্দ্বী সেখানে সেই ksh93 গুলি ব্যবহার করে না ।pipe(2)
ঠিক আছে, নামী পাইপ ( 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
একই কাজ করে, খাটো এবং আরও বহনযোগ্য।
prog1 < fifo | prog2 > fifo।
prog1 < fifo | tee /dev/stderr | prog2 | tee /dev/stderr > fifo।
prog2 < fifo0 > fifo1হবে তখন আপনি আপনার সামান্য নাচ এড়াতে পারেন exec 30< ...(যা প্রণালী দ্বারা শুধুমাত্র সঙ্গে কাজ করে bashবা yashযে মত 10 fds জন্য)।
dashখুব ঠিক আছে বলে মনে হয় (তবে কিছুটা আলাদা আচরণ করে)
আপনি যা করার চেষ্টা করছেন এটি এটি কিনা আমি নিশ্চিত নই:
nc -l -p 8096 -c second &
nc -c first 127.0.0.1 8096 &
এটি 8096 পোর্টে একটি শ্রবণকারী সকেট খোলার মাধ্যমে শুরু হয় এবং একবার সংযোগ স্থাপন হয়ে গেলে এর সাথে স্ট্রিম আউটপুট এবং স্ট্রিম ইনপুট হিসাবে প্রোগ্রাম তৈরি secondহয় ।stdinstdout
তারপরে, একটি সেকেন্ড ncচালু করা হয়েছে যা শ্রবণ পোর্টের সাথে সংযোগ স্থাপন করে এবং প্রবাহের ইনপুট হিসাবে এবং স্ট্রিম আউটপুট হিসাবে এর firstসাথে প্রোগ্রাম তৈরি করে ।stdoutstdin
এটি কোনও পাইপ ব্যবহার করে ঠিক করা হয় নি তবে এটি আপনার যা প্রয়োজন তা করা মনে হচ্ছে।
এটি যেমন নেটওয়ার্কটি ব্যবহার করে, এটি 2 টি দূরবর্তী কম্পিউটারে করা যেতে পারে। ওয়েব সার্ভার ( second) এবং একটি ওয়েব ব্রাউজার ( first) কাজ করার প্রায় এটিই ।
nc -Uইউনিক্স ডোমেন সকেটের জন্য যা কেবল ফাইল সিস্টেমের ঠিকানা স্থান নেয় space
আপনি পাইপেক্সেক ব্যবহার করতে পারেন :
$ pipexec -- '[A' cmd1 ] '[B' cmd2 ] '{A:1>B:0}' '{B:1>A:0}'
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
এটা করে।
এই জাতীয় দ্বি নির্দেশমূলক পাইপ লেখার জন্য একটি সুবিধাজনক বিল্ডিং ব্লক এমন একটি বিষয় যা বর্তমান প্রক্রিয়ার স্টাডাউট এবং স্টিডিনকে একসাথে সংযুক্ত করে। আসুন আমরা এটিকে আইওলোপ বলি। এই ফাংশনটি কল করার পরে আপনার কেবল নিয়মিত পাইপ শুরু করতে হবে:
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
}
নামকরণ করা পাইপটি আইওলাপ সেটআপের সময় কেবলমাত্র ফাইল সিস্টেমে উপস্থিত রয়েছে। এই ফাংশনটি বেশ পসিক্স নয় কারণ এমকেটেম্প অবমূল্যায়ন করা হয়েছে (এবং কোনও রেস আক্রমণের পক্ষে সম্ভাব্য ঝুঁকিপূর্ণ)।
/ প্রোক / ব্যবহার করে একটি লিনাক্স-নির্দিষ্ট প্রয়োগ সম্ভব হয় যার জন্য নামযুক্ত পাইপের প্রয়োজন হয় না তবে আমি মনে করি এটি যথেষ্ট ভালের চেয়ে বেশি।
( : <$FIFO & )আরও বিশদে ব্যাখ্যা করে যুক্ত করা হয়েছে । পোস্ট করার জন্য আপনাকে ধন্যবাদ।
mktemp? আমি এটি ব্যাপকভাবে ব্যবহার করি এবং যদি কোনও নতুন সরঞ্জাম এটির জায়গা করে নিয়ে থাকে তবে আমি এটি ব্যবহার শুরু করতে চাই।
এছাড়াও আছে
dpipe, "দ্বি-দিকনির্দেশক পাইপ", ভিডি 2 প্যাকেজে অন্তর্ভুক্ত এবং বর্তমান ডিস্ট্রো প্যাকেজ পরিচালনা ব্যবস্থায় অন্তর্ভুক্ত ।
dpipe processA = processB
সকেট , সংযুক্ত সবকিছু-থেকে-সর্বদা সরঞ্জাম tool
socat EXEC:Program1 EXEC:Program2
যেহেতু @ স্টাফেনচাজেলা মন্তব্যগুলিতে সঠিকভাবে নোট করেছেন, উপরের উদাহরণগুলি "বেস ফর্ম", অনুরূপ প্রশ্নের উত্তর দেওয়ার জন্য তার বিকল্পগুলির সাথে চমৎকার উদাহরণ রয়েছে ।
socatপাইপের পরিবর্তে সকেট ব্যবহার করে (আপনি এটি দিয়ে পরিবর্তন করতে পারেন commtype=pipes)। আপনি noforkপাইপ / সকেটের মধ্যে অতিরিক্ত সোকা প্রসেসটি মুভিং ডেটা এড়াতে বিকল্পটি যুক্ত করতে চাইতে পারেন । ( আমার উত্তর বিটিডব্লিউতে সম্পাদনার জন্য ধন্যবাদ )
এখানে অনেক দুর্দান্ত উত্তর রয়েছে। তাই আমি তাদের সাথে চারপাশে সহজ খেলতে কিছু যুক্ত করতে চাই। আমি ধরে নিই 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
...