হ্যাঁ, আমরা বেশ কয়েকটি জিনিস দেখতে পাই:
while read line; do
echo $line | cut -c3
done
বা তার থেকেও খারাপ:
for line in `cat file`; do
foo=`echo $line | awk '{print $2}'`
echo whatever $foo
done
(হাসবেন না, আমি তাদের অনেকগুলি দেখেছি)।
সাধারণত শেল স্ক্রিপ্টিং প্রাথমিক থেকে। সেগুলি সি বা পাইথনের মতো অপরিহার্য ভাষাগুলিতে আপনি কী করবেন তার নিখুঁত আক্ষরিক অনুবাদ, তবে এটি আপনি শেলগুলিতে কীভাবে করেন না এবং সেই উদাহরণগুলি খুব অদক্ষ, সম্পূর্ণ অবিশ্বাস্য (সম্ভাব্যভাবে সুরক্ষার সমস্যার দিকে নিয়ে যায়), এবং যদি আপনি কখনও পরিচালনা করেন বেশিরভাগ বাগগুলি ঠিক করার জন্য, আপনার কোডটি অযৌক্তিক হয়ে যায়।
ধারণার দিক থেকে
সি বা অন্যান্য ভাষায়, বিল্ডিং ব্লকগুলি কম্পিউটারের নির্দেশাবলীর ঠিক এক স্তরের উপরে। আপনি আপনার প্রসেসরকে বলবেন কী করবেন এবং তারপরে আর কি করবেন। আপনি নিজের প্রসেসরটি হাত দিয়ে নিয়ে যান এবং মাইক্রো ম্যানেজ করেন: আপনি সেই ফাইলটি খোলেন, আপনি পড়েন যে অনেকগুলি বাইট, আপনি এটি করেন, আপনি এটি দিয়ে এটি করেন।
শেলগুলি একটি উচ্চ স্তরের ভাষা। কেউ বলতে পারে এটি একটি ভাষাও নয়। তারা সমস্ত কমান্ড লাইন দোভাষী এর আগে। আপনি যে কমান্ডগুলি চালাচ্ছেন সেগুলি দিয়ে কাজটি করা হয় এবং শেলটি কেবল তাদের অর্কেস্ট্রেট করার উদ্দেশ্যে হয়।
ইউনিক্স যে দুর্দান্ত জিনিসগুলির সূচনা করেছিল তার মধ্যে একটি হ'ল পাইপ এবং সেগুলি ডিফল্ট স্টিডিন / স্টিডআউট / স্টডার স্ট্রিম যা সমস্ত কমান্ড ডিফল্টরূপে হ্যান্ডেল করে।
৪৫ বছরে, আমরা কমান্ডের শক্তিকে বাড়িয়ে তুলতে এবং কোনও কার্যক্রমে তাদের সহযোগিতা করার জন্য সেই API এর চেয়ে ভাল পাইনি। লোকেরা আজও শেল ব্যবহার করছে বলে সম্ভবত এটিই প্রধান কারণ।
আপনার কাছে একটি কাটিয়া সরঞ্জাম এবং প্রতিবর্ণী সরঞ্জাম পাওয়া গেছে এবং আপনি কেবল এটি করতে পারেন:
cut -c4-5 < in | tr a b > out
শেলটি কেবল নদীর গভীরতানির্ণয় করছে (ফাইলগুলি খুলুন, পাইপগুলি সেটআপ করুন, আদেশগুলি আহবান করুন) এবং যখন এটি সব প্রস্তুত হয়, তখন শেলটি কিছু না করে কেবল প্রবাহিত হয়। সরঞ্জামগুলি তাদের কাজ একই সাথে করে, দক্ষতার সাথে তাদের নিজস্ব গতিতে পর্যাপ্ত পরিমাণে বাফারিং করে যাতে একজন অন্যকে অবরুদ্ধ করে না, এটি কেবল সুন্দর এবং তত সহজ।
কোনও সরঞ্জাম চালাতে যদিও ব্যয় হয় (এবং আমরা এটি পারফরম্যান্স পয়েন্টে বিকাশ করব)। সিগুলিতে হাজার হাজার নির্দেশাবলীর সাহায্যে সেই সরঞ্জামগুলি লিখিত হতে পারে একটি প্রক্রিয়া তৈরি করতে হবে, সরঞ্জামটি লোড করতে হবে, শুরু করতে হবে, তারপর পরিষ্কার-পরিচ্ছন্ন করতে হবে, প্রক্রিয়াটি ধ্বংস হয়ে যেতে হবে এবং অপেক্ষা করতে হবে।
চালনা cutরান্নাঘরের ড্রয়ার খোলার মতো, ছুরিটি নিন, এটি ব্যবহার করুন, ধুয়ে ফেলুন, শুকিয়ে নিন, আবার ড্রয়ারে রেখে দিন। যখন তুমি কর:
while read line; do
echo $line | cut -c3
done < file
এটি ফাইলের প্রতিটি লাইনের মতো, readরান্নাঘরের ড্রয়ার থেকে সরঞ্জাম পেয়ে যাওয়া (খুব আনাড়ি কারণ এটি এর জন্য ডিজাইন করা হয়নি ), একটি লাইন পড়ুন, আপনার পড়ার সরঞ্জামটি ধুয়ে ফেলুন, আবার ড্রয়ারে রেখে দিন। তারপরে echoএবং cutসরঞ্জামটির জন্য একটি সভা নির্ধারণ করুন, এটিকে ড্রয়ার থেকে এনে ডেকে আনুন, তাদের ধুয়ে ফেলুন, শুকিয়ে নিন, আবার ড্রয়ারে রেখে দিন।
সেগুলির কয়েকটি ( readএবং echo) বেশিরভাগ শেলের মধ্যে নির্মিত, তবে যেহেতু এখানে খুব কমই একটি পার্থক্য তৈরি হয়েছে echoএবং cutএখনও পৃথক প্রক্রিয়াতে চালানো দরকার।
এটি পেঁয়াজ কাটার মতো তবে আপনার ছুরি ধুয়ে প্রতিটি ফালিগুলির মধ্যে রান্নাঘরের ড্রয়ারে রেখে দিন।
এখানে স্পষ্ট উপায় হ'ল cutড্রয়ার থেকে আপনার সরঞ্জামটি পাওয়া, আপনার পুরো পেঁয়াজ কেটে টুকরো টুকরো করে পুরো কাজটি শেষ করার পরে ড্রয়ারে এটি আবার রেখে দেওয়া।
আইডাব্লু, শেলগুলিতে, বিশেষত পাঠ্য প্রক্রিয়া করার জন্য, আপনি যথাসম্ভব কয়েকটি ইউটিলিটিটি আহ্বান করুন এবং তাদেরকে কার্যটিতে সহযোগিতা করতে বলুন, পরেরটি চালানোর আগে প্রতিটিটি শুরু করার জন্য, চালানোর জন্য, পরিষ্কার করার জন্য ক্রম সহ কয়েক হাজার সরঞ্জাম চালাবেন না।
ব্রুস এর সূক্ষ্ম উত্তরে আরও পড়া । শেলগুলিতে নিম্ন-স্তরের পাঠ্য প্রক্রিয়াজাতকরণ অভ্যন্তরীণ সরঞ্জামগুলি (সম্ভবত এটি ব্যতীত zsh) সীমাবদ্ধ, জটিল এবং সাধারণ পাঠ্য প্রক্রিয়াকরণের জন্য সাধারণত ফিট হয় না।
কর্মক্ষমতা
যেমনটি আগেই বলা হয়েছিল, একটি কমান্ড চালাতে একটি ব্যয় হয়। যদি এই কমান্ডটি অন্তর্নির্মিত না হয় তবে একটি বিশাল ব্যয়, তবে সেগুলি বিল্ট ইন হলেও, ব্যয়টি বড়।
এবং শেলগুলি সেভাবে চলার জন্য ডিজাইন করা হয়নি, তাদের পারফরম্যান্ট প্রোগ্রামিং ভাষা হওয়ার প্রবণতা নেই। তারা নয়, তারা কেবল কমান্ড লাইন ইন্টারপ্রেটার। সুতরাং, এই ফ্রন্টে সামান্য অপ্টিমাইজেশন করা হয়েছে।
এছাড়াও, শেলগুলি পৃথক প্রক্রিয়াতে কমান্ড চালায়। এই বিল্ডিং ব্লকগুলি একটি সাধারণ স্মৃতি বা রাজ্য ভাগ করে না। আপনি যখন সি fgets()বা একটি fputs()করেন, এটি স্টিডিওতে একটি ফাংশন। ব্যয়বহুল সিস্টেমের কলটি প্রায়শই করা এড়াতে, স্টাডিও সমস্ত স্টিডিও ফাংশনের জন্য ইনপুট এবং আউটপুট দেওয়ার জন্য অভ্যন্তরীণ বাফার রাখে।
সংশ্লিষ্ট এমনকি builtin শেল ইউটিলিটি ( read, echo, printf) যে করতে পারবেন না। readএকটি লাইন পড়া বোঝানো হয়। এটি যদি নতুন লাইনের চরিত্রটি পড়ে যায় তবে এর অর্থ হল আপনি চালিত পরবর্তী আদেশটি এটি মিস করবে। সুতরাং readএকবারে ইনপুটটি একটি বাইটটি পড়তে হবে (কিছু প্রয়োগের একটি অপ্টিমাইজেশন থাকে যদি ইনপুটটি নিয়মিত ফাইল থাকে যাতে তারা খণ্ডগুলি পড়ে এবং ফিরে চাওয়া হয় তবে এটি কেবল নিয়মিত ফাইলগুলির জন্য কাজ করে এবং bashউদাহরণস্বরূপ কেবল 128 বাইট খণ্ডগুলি পড়ে যা পাঠ্য ইউটিলিটিগুলি এর চেয়ে অনেক কম হবে)।
আউটপুট দিকে একই, echoএটির আউটপুটটি কেবল বাফার করতে পারে না, সরাসরি এটি আউটপুট দিতে হয় কারণ আপনি যে পরবর্তী কমান্ডটি চালান সে বাফারটি ভাগ করে নেবে না।
স্পষ্টতই, ক্রমানুসারে কমান্ডগুলি চালনার অর্থ আপনার তাদের জন্য অপেক্ষা করতে হবে, এটি সামান্য শিডিয়ুলার নৃত্য যা শেল এবং সরঞ্জাম এবং পিছনে নিয়ন্ত্রণ দেয়। এর অর্থও (পাইপলাইনে সরঞ্জামগুলির দীর্ঘকালীন চলমান দৃষ্টান্তগুলি ব্যবহার করার বিপরীতে) যে আপনি যখন উপলব্ধ তখন একই সাথে বেশ কয়েকটি প্রসেসরের ক্ষতি করতে পারবেন না।
আমার দ্রুত পরীক্ষায় সেই while readলুপ এবং (অনুমিত) সমতুল্যের cut -c3 < fileমধ্যে আমার পরীক্ষাগুলিতে সিপিইউ সময়ের অনুপাত 40000 (অর্ধ দিনের তুলনায় এক সেকেন্ড) থাকে ratio এমনকি যদি আপনি কেবল শেল বিল্টিন ব্যবহার করেন:
while read line; do
echo ${line:2:1}
done
(এখানে এখানে bash), এটি এখনও প্রায় 1: 600 (এক সেকেন্ড বনাম 10 মিনিট) এর কাছাকাছি।
নির্ভরযোগ্যতা / স্পষ্টতা
এই কোডটি সঠিকভাবে পাওয়া খুব কঠিন। আমি যে উদাহরণগুলি দিয়েছি তা বন্যগুলিতে প্রায়শই দেখা যায় তবে তাদের অনেকগুলি বাগ রয়েছে।
readএকটি সহজ সরঞ্জাম যা বিভিন্ন বিভিন্ন জিনিস করতে পারে। এটি ব্যবহারকারীর কাছ থেকে ইনপুট পড়তে পারে, শব্দগুলিতে বিভক্ত করে বিভিন্ন ভেরিয়েবলগুলিতে সঞ্চয় করতে পারে। read lineনেই না ইনপুট একটি লাইন পড়া, হয়তো বা এটি একটি খুব বিশেষ পদ্ধতিতে একটি লাইন পড়ে। এটি প্রকৃতপক্ষে শব্দগুলিকে ইনপুট থেকে শব্দগুলি পড়ে এবং সেই শব্দগুলি পৃথক করে $IFSএবং যেখানে ব্যাকস্ল্যাশ পৃথককারী বা নিউলাইন চরিত্রের হাত থেকে বাঁচতে ব্যবহৃত হতে পারে।
এর ইনপুটটিতে এর ডিফল্ট মান সহ $IFS:
foo\/bar \
baz
biz
read lineসংরক্ষণ করবে "foo/bar baz"মধ্যে $line, না " foo\/bar \"হিসাবে আপনি আশা চাই।
একটি লাইন পড়তে আপনার আসলে প্রয়োজন:
IFS= read -r line
এটি খুব স্বজ্ঞাত নয়, তবে এটি এটিই মনে রাখবেন, শেলগুলি ব্যবহার করা হয়নি to
একই জন্য echo। echoক্রম প্রসারিত। আপনি এলোমেলো ফাইলের বিষয়বস্তুর মতো স্বেচ্ছাচারী বিষয়বস্তুর জন্য এটি ব্যবহার করতে পারবেন না। printfপরিবর্তে এখানে আপনার প্রয়োজন ।
এবং অবশ্যই, এখানে আপনার ভেরিয়েবলের উদ্ধৃতি দেওয়া সাধারণত ভুলে যাওয়া যা প্রত্যেকের মধ্যে পড়ে। সুতরাং এটি আরও:
while IFS= read -r line; do
printf '%s\n' "$line" | cut -c3
done < file
এখন, আরও কয়েকটি সতর্কতা:
- ছাড়া
zsh, যে অন্তত গনুহ টেক্সট ইউটিলিটি সমস্যা হতো না যদি ইনপুট NUL অক্ষর রয়েছে কাজ করে না।
- শেষ নিউলাইনের পরে যদি ডেটা থাকে তবে তা এড়িয়ে যাবে
- লুপের ভিতরে, স্টিডিন পুনঃনির্দেশিত হয় যাতে আপনার মনোযোগ দেওয়া উচিত যে এতে থাকা আদেশগুলি স্টিডিন থেকে না পড়ে।
- লুপগুলির মধ্যে থাকা কমান্ডগুলির জন্য, আমরা সেগুলি সফল হয় কি না সেদিকে আমরা মনোযোগ দিচ্ছি না। সাধারণত, ত্রুটি (ডিস্ক পূর্ণ, ত্রুটিগুলি পড়ার ...) শর্তগুলি সঠিকভাবে সমতুল্যতার চেয়ে খারাপভাবে পরিচালিত হবে ।
আমরা যদি উপরের কয়েকটি বিষয়কে সম্বোধন করতে চাই তবে তা হয়ে ওঠে:
while IFS= read -r line <&3; do
{
printf '%s\n' "$line" | cut -c3 || exit
} 3<&-
done 3< file
if [ -n "$line" ]; then
printf '%s' "$line" | cut -c3 || exit
fi
এটাই কম-বেশি স্পষ্ট হয়ে উঠছে।
আর্গুমেন্টের মাধ্যমে কমান্ডগুলিতে ডেটা পাঠানো বা ভেরিয়েবলগুলিতে তাদের আউটপুট পুনরুদ্ধার করা সহ আরও বেশ কয়েকটি সমস্যা রয়েছে:
- আর্গুমেন্টের আকারের সীমাবদ্ধতা (কিছু পাঠ্য ইউটিলিটি বাস্তবায়নের একটি সীমাও রয়েছে, যদিও তাদের কাছে পৌঁছানোর প্রভাবটি সাধারণত কম সমস্যাযুক্ত)
- NUL অক্ষর (পাঠ্য ইউটিলিটিগুলির সাথেও সমস্যা)।
- যুক্তিগুলি বিকল্প হিসাবে নেওয়া হয় যখন তারা শুরু করে
-(বা +কখনও কখনও)
- বিভিন্ন কমান্ডের বিভিন্ন quirks সাধারণত এই লুপগুলিতে ব্যবহৃত হয়
expr, test...
- অসম্পূর্ণ উপায়ে মাল্টি-বাইট অক্ষর পরিচালনা করে এমন বিভিন্ন শেলের (সীমিত) পাঠ্য ম্যানিপুলেশন অপারেটরগুলি।
- ...
সুরক্ষা বিবেচনা
যখন আপনি শেল ভেরিয়েবল এবং কমান্ডগুলির সাথে যুক্তি দিয়ে কাজ শুরু করেন , আপনি একটি খনি ক্ষেত্রের মধ্যে প্রবেশ করছেন।
আপনি যদি ভেরিয়েবলগুলি উদ্ধৃত করতে ভুলে যান তবে বিকল্প চিহ্নিতকারীটির সমাপ্তিটি ভুলে যান , বহু-বাইট অক্ষর (এই দিনগুলির আদর্শ) দিয়ে লোকেলগুলিতে কাজ করুন, আপনি যে বাগগুলি তাড়াতাড়ি বা পরে দুর্বলতায় পরিণত করবেন তা নিশ্চিত করতেই পারেন।
আপনি যখন লুপগুলি ব্যবহার করতে পারেন।
TBD
yes?