আপনি গনুহ stdbuf ও সংমিশ্রণ ব্যবহার করতে পারে pee
থেকে moreutils :
echo "Hello world!" | stdbuf -o 1M pee cmd1 cmd2 cmd3 > output
প্রস্রাব popen(3)
যারা 3 শেল কমান্ড লাইন গুলি এবং তারপর fread
ইনপুট s এবং fwrite
এটি গুলি সব তিনটি, যা আপ 1M করতে বাফার করা হবে না।
ধারণাটি অন্তত ইনপুট হিসাবে বড় হিসাবে একটি বাফার করা উচিত। এই ভাবে তিনটি কমান্ড একই সাথে শুরু করা হলেও তারা কেবল pee
pclose
তিনটি কমান্ড ক্রমিকভাবে ইনপুট আসতে দেখবে ।
প্রত্যেকটির উপর pclose
, pee
বাফারটি কমান্ডে ফ্লাশ করে এবং এটি সমাপ্তির জন্য অপেক্ষা করে। এটি গ্যারান্টি দেয় যে যতক্ষণ cmdx
না এই কমান্ডগুলি কোনও ইনপুট পাওয়ার আগে কিছু আউটপুট শুরু না করে (এবং তাদের পিতামাতার ফিরে আসার পরে আউটপুট চালিয়ে যেতে পারে এমন প্রক্রিয়াটি কাঁটাচামচ করবেন না), তিনটি কমান্ডের আউটপুট হবে না ইন্টারলিভড্।
বাস্তবে, এটি 3 টি কমান্ড একই সাথে শুরু হওয়া ত্রুটি সহ স্মৃতিতে একটি টেম্প ফাইল ব্যবহার করার মতো।
একই সাথে কমান্ডগুলি শুরু করা এড়াতে, আপনি pee
শেল ফাংশন হিসাবে লিখতে পারেন :
pee() (
input=$(cat; echo .)
for i do
printf %s "${input%.}" | eval "$i"
done
)
echo "Hello world!" | pee cmd1 cmd2 cmd3 > out
তবে সাবধান থাকুন যে zsh
এনওএল অক্ষরের সাথে বাইনারি ইনপুট ব্যতীত অন্য শেলগুলি ব্যর্থ হবে।
এটি অস্থায়ী ফাইলগুলি ব্যবহার করা এড়িয়ে যায়, তবে এর অর্থ পুরো ইনপুট মেমরিতে সঞ্চয় করা থাকে।
যাই হোক না কেন, আপনাকে ইনপুটটি কোথাও, মেমোরিতে বা কোনও টেম্প ফাইলে সঞ্চয় করতে হবে।
প্রকৃতপক্ষে, এটি বেশ আকর্ষণীয় প্রশ্ন, কারণ এটি আমাদের একক কার্যক্রমে বেশ কয়েকটি সহজ সরঞ্জামকে সহযোগিতা করার ইউনিক্স ধারণার সীমা দেখায়।
এখানে, আমরা কাজটিতে বেশ কয়েকটি সরঞ্জাম সহযোগিতা করতে চাই:
- উত্স কমান্ড (এখানে
echo
)
- একটি প্রেরণকারী আদেশ (
tee
)
- কিছু ফিল্টার কমান্ড (
cmd1
, cmd2
, cmd3
)
- এবং একটি সমষ্টি কমান্ড (
cat
)।
তারা সকলেই একই সাথে চলতে পারত এবং যে ডেটা পাওয়া যায় তার সাথে সাথে প্রক্রিয়া করার জন্য যে ডেটা বোঝায় তা তাদের কঠোর পরিশ্রম করতে পারলে ভাল লাগবে।
একটি ফিল্টার কমান্ডের ক্ষেত্রে এটি সহজ:
src | tee | cmd1 | cat
সমস্ত কমান্ড একই সাথে চালিত হয়, এটি উপলভ্য হওয়ার সাথে সাথে cmd1
ডেটা গুটি করা শুরু করে src
।
তিনটি ফিল্টার কমান্ডের সাহায্যে আমরা এখনও একই কাজ করতে পারি: সেগুলি একযোগে শুরু করুন এবং পাইপগুলির সাথে তাদের সংযুক্ত করুন:
┏━━━┓▁▁▁▁▁▁▁▁▁▁┏━━━━┓▁▁▁▁▁▁▁▁▁▁┏━━━┓
┃ ┃░░░░2░░░░░┃cmd1┃░░░░░5░░░░┃ ┃
┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃
┏━━━┓▁▁▁▁▁▁▁▁▁▁┃ ┃▁▁▁▁▁▁▁▁▁▁┏━━━━┓▁▁▁▁▁▁▁▁▁▁┃ ┃▁▁▁▁▁▁▁▁▁┏━━━┓
┃src┃░░░░1░░░░░┃tee┃░░░░3░░░░░┃cmd2┃░░░░░6░░░░┃cat┃░░░░░░░░░┃out┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔┗━━━┛
┃ ┃▁▁▁▁▁▁▁▁▁▁┏━━━━┓▁▁▁▁▁▁▁▁▁▁┃ ┃
┃ ┃░░░░4░░░░░┃cmd3┃░░░░░7░░░░┃ ┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━┛
যা আমরা নামযুক্ত পাইপগুলির সাথে তুলনামূলকভাবে সহজেই করতে পারি :
pee() (
mkfifo tee-cmd1 tee-cmd2 tee-cmd3 cmd1-cat cmd2-cat cmd3-cat
{ tee tee-cmd1 tee-cmd2 tee-cmd3 > /dev/null <&3 3<&- & } 3<&0
eval "$1 < tee-cmd1 1<> cmd1-cat &"
eval "$2 < tee-cmd2 1<> cmd2-cat &"
eval "$3 < tee-cmd3 1<> cmd3-cat &"
exec cat cmd1-cat cmd2-cat cmd3-cat
)
echo abc | pee 'tr a A' 'tr b B' 'tr c C'
(সর্বোপরি এই } 3<&0
বিষয়টির চারপাশে কাজ করা হয় যে &
থেকে পুনঃনির্দেশ stdin
হয় /dev/null
, এবং আমরা <>
পাইপগুলি অপর প্রান্তটি অবধি অবরুদ্ধ হওয়া অবধি এড়িয়ে চলার জন্য ব্যবহার করি cat
))
বা নামক পাইপগুলি এড়ানোর জন্য, কিছুটা বেশি বেদনাদায়কভাবে zsh
কপোক্রোক সহ:
pee() (
n=0 ci= co= is=() os=()
for cmd do
eval "coproc $cmd $ci $co"
exec {i}<&p {o}>&p
is+=($i) os+=($o)
eval i$n=$i o$n=$o
ci+=" {i$n}<&-" co+=" {o$n}>&-"
((n++))
done
coproc :
read -p
eval tee /dev/fd/$^os $ci "> /dev/null &" exec cat /dev/fd/$^is $co
)
echo abc | pee 'tr a A' 'tr b B' 'tr c C'
এখন, প্রশ্নটি হল: একবার সমস্ত প্রোগ্রাম শুরু হয়ে সংযুক্ত হয়ে গেলে কি ডেটা প্রবাহিত হবে?
আমরা দুটি কন্ট্রেন্ট পেয়েছি:
tee
এর সমস্ত আউটপুট একই হারে ফিড দেয়, তাই এটি কেবল তার ধীর আউটপুট পাইপের হারে ডেটা প্রেরণ করতে পারে।
cat
কেবলমাত্র দ্বিতীয় পাইপ (উপরের অঙ্কনের 6 টি পাইপ) থেকে পড়া শুরু হবে যখন সমস্ত ডেটা প্রথম (5) থেকে পড়েছে।
এর অর্থ হ'ল শেষ না হওয়া পর্যন্ত ডেটা পাইপ 6 এ প্রবাহিত হবে না cmd1
। এবং, উপরেরটির মতো tr b B
এটিরও অর্থ এই হতে পারে যে ডেটা পাইপ 3 এ প্রবাহিত হবে না, যার অর্থ এটি 3, 3 বা 4 এর পাইপগুলির কোনওটিতে প্রবাহিত হবে না কারণ tee
সমস্ত 3 এর সর্বনিম্ন হারে ফিড দেয়।
অনুশীলনে এই পাইপগুলির একটি নন-নাল আকার রয়েছে, তাই কিছু ডেটাগুলি পরিচালনা করার জন্য পরিচালনা করবে এবং আমার সিস্টেমে কমপক্ষে, আমি এটি পর্যন্ত কাজ করতে পারি:
yes abc | head -c $((2 * 65536 + 8192)) | pee 'tr a A' 'tr b B' 'tr c C' | uniq -c -c
এর বাইরেও, সাথে
yes abc | head -c $((2 * 65536 + 8192 + 1)) | pee 'tr a A' 'tr b B' 'tr c C' | uniq -c
আমরা একটি অচলাবস্থা পেয়েছি, যেখানে আমরা এই পরিস্থিতিতে আছি:
┏━━━┓▁▁▁▁2▁▁▁▁▁┏━━━━┓▁▁▁▁▁5▁▁▁▁┏━━━┓
┃ ┃░░░░░░░░░░┃cmd1┃░░░░░░░░░░┃ ┃
┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃
┏━━━┓▁▁▁▁1▁▁▁▁▁┃ ┃▁▁▁▁3▁▁▁▁▁┏━━━━┓▁▁▁▁▁6▁▁▁▁┃ ┃▁▁▁▁▁▁▁▁▁┏━━━┓
┃src┃██████████┃tee┃██████████┃cmd2┃██████████┃cat┃░░░░░░░░░┃out┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┃ ┃▔▔▔▔▔▔▔▔▔┗━━━┛
┃ ┃▁▁▁▁4▁▁▁▁▁┏━━━━┓▁▁▁▁▁7▁▁▁▁┃ ┃
┃ ┃██████████┃cmd3┃██████████┃ ┃
┗━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━━┛▔▔▔▔▔▔▔▔▔▔┗━━━┛
আমরা পাইপ 3 এবং 6 (প্রতিটি 64kiB) পূরণ করেছি। tee
যে অতিরিক্ত বাইট পড়েছে, এটি এটি খাওয়ানো হয়েছে cmd1
, কিন্তু
- এটি
cmd2
খালি করার জন্য অপেক্ষা করায় এটি এখন পাইপ 3 এ লেখা অবরুদ্ধ
cmd2
এটি খালি করতে পারে না কারণ এটি পাইপ on এ লেখা অবরুদ্ধ রয়েছে, cat
এটি খালি করার জন্য অপেক্ষা করছে
cat
এটি খালি করতে পারে না কারণ এটি অপেক্ষা করছে যে পাইপ 5 এ আর কোনও ইনপুট নেই।
cmd1
cat
আর কোনও ইনপুট নেই তা বলতে পারে না কারণ এটি থেকে আরও ইনপুটটির জন্য নিজেকে অপেক্ষা করছে tee
।
- এবং কোনও ইনপুট নেই
tee
তা বলতে পারে না cmd1
কারণ এটি ব্লক করা হয়েছে ... ইত্যাদি।
আমরা একটি নির্ভরতা লুপ পেয়েছি এবং এইভাবে একটি অচলাবস্থা রয়েছে।
এখন, সমাধান কি? বড় পাইপ 3 এবং 4 (এর সমস্ত src
আউটপুট ধারণ করতে যথেষ্ট বড় ) এটি করবে। উদাহরণস্বরূপ আমরা এর pv -qB 1G
মধ্যে সন্নিবেশ করে tee
এবং cmd2/3
যেখানে pv
1G পর্যন্ত ডেটা অপেক্ষা করতে cmd2
এবং cmd3
সেগুলি পড়তে সঞ্চয় করতে পারি। এর অর্থ দুটি বিষয় হবে যদিও:
- এটি সম্ভবত মেমরির প্রচুর পরিমাণে ব্যবহার করছে এবং তদ্রূপ, এটি সদৃশ
- এটি সমস্ত 3 টি কমান্ড সহযোগিতা করতে ব্যর্থ হয়েছে কারণ
cmd2
বাস্তবে কেবলমাত্র যখন cmd1 শেষ হবে তখন ডেটা প্রক্রিয়াকরণ শুরু হবে।
দ্বিতীয় সমস্যার সমাধান হ'ল পাইপগুলি 6 এবং 7 টি আরও বড় করা। ধরে নিই cmd2
এবং cmd3
তারা যতটা আউটপুট খায়, তার বেশি স্মৃতি গ্রাহ্য হবে না।
ডেটাটিকে অনুলিপি করা এড়ানোর একমাত্র উপায় হ'ল প্রেরক নিজেই ডেটা ধরে রাখা বাস্তবায়ন করা, এটি তারতম্যকে tee
দ্রুততম আউটপুটের হারে ফিড সরবরাহ করতে পারে (ফিড সরবরাহ করার জন্য ডেটা ধারণ করে) ধীরে ধীরে তাদের নিজস্ব গতিতে)। আসলেই তুচ্ছ নয়।
সুতরাং, শেষ পর্যন্ত, প্রোগ্রামিং ছাড়াই যুক্তিসঙ্গতভাবে আমরা সর্বোত্তমভাবে পেতে পারি সম্ভবত এটির মতো (জেডএস সিনট্যাক্স):
max_hold=1G
pee() (
n=0 ci= co= is=() os=()
for cmd do
if ((n)); then
eval "coproc pv -qB $max_hold $ci $co | $cmd $ci $co | pv -qB $max_hold $ci $co"
else
eval "coproc $cmd $ci $co"
fi
exec {i}<&p {o}>&p
is+=($i) os+=($o)
eval i$n=$i o$n=$o
ci+=" {i$n}<&-" co+=" {o$n}>&-"
((n++))
done
coproc :
read -p
eval tee /dev/fd/$^os $ci "> /dev/null &" exec cat /dev/fd/$^is $co
)
yes abc | head -n 1000000 | pee 'tr a A' 'tr b B' 'tr c C' | uniq -c