Bash একটি শক্তিশালী প্রোগ্রামিং ল্যাঙ্গুয়েজ, যা কমান্ড লাইন এবং শেল স্ক্রিপ্টে ব্যবহারের জন্য খুব ভালোভাবে ডিজাইন করা। এই তিন-ভাগের সিরিজ, যা আমার তিন-খণ্ডের লিনাক্স সেলফ-স্টাডি কোর্সের ওপর ভিত্তি করে, কমান্ড-লাইন ইন্টারফেস (CLI)-এ Bash কে প্রোগ্রামিং ল্যাঙ্গুয়েজ হিসেবে ব্যবহার করার ওপর আলোকপাত করে।
সিরিজের প্রথম আর্টিকেলে Bash-এ সিম্পল কমান্ড-লাইন প্রোগ্রামিং আলোচনা করা হয়েছে, ভেরিয়েবল এবং কন্ট্রোল অপারেটর সহ। দ্বিতীয় আর্টিকেলে ফাইল, স্ট্রিং, न्यूमেরিক এবং মিসিলেনিয়াস লজিক্যাল অপারেটর ও Bash-এ বিভিন্ন টাইপের শেল এক্সপ্যানশন দেখানো হয়েছে। এই তৃতীয় (শেষ) আর্টিকেলে লুপের ব্যবহার এবং সেগুলো কন্ট্রোল করার পদ্ধতি আলোচনা করা হবে।
লুপ (Loops)
আমি যেসব প্রোগ্রামিং ল্যাঙ্গুয়েজ ব্যবহার করেছি, সবগুলোতে কমপক্ষে একাধিক টাইপের লুপ স্ট্রাকচার রয়েছে যা পুনরাবৃত্তি অপারেশন انجام দেয়। আমি for loop খুব বেশি ব্যবহার করি, কিন্তু while এবং until লুপও খুব কাজের।
for loops
Bash-র for কমান্ডের ইমপ্লিমেন্টেশন, আমার মতে, বেশিরভাগের চেয়ে বেশি ফ্লেক্সিবল কারণ এটি নন-নিউমেরিক ভ্যালু হ্যান্ডল করতে পারে; বিপরীতে, স্ট্যান্ডার্ড C ল্যাঙ্গুয়েজের for লুপ শুধু নিউমেরিক ভ্যালু হ্যান্ডল করতে পারে।
Bash সংস্করণের for কমান্ডের বেসিক স্ট্রাকচার সহজ:
for Var in list1 ; do list2 ; done
এটি বোঝায়: "list1-এ থাকা প্রতিটি ভ্যালুর জন্য, $Var কে সেট ভ্যালুতে সেট করুন এবং তারপর list2-র প্রোগ্রাম স্টেটমেন্টগুলো সেট ভ্যালু দিয়ে এক্সিকিউট করুন; list1-এর সকল ভ্যালু ইউজ হয়ে গেলে লুপ থেকে বের হয়ে যান।" list1-এ থাকা ভ্যালুগুলো এক্সপ্লিসিট স্ট্রিং হতে পারে, অথবা কমান্ড সাবস্টিটিউশনের রেজাল্ট (সিরিজের দ্বিতীয় আর্টিকেলে বর্ণিত)। আমি এই কনস্ট্রাক্ট খুব বেশি ব্যবহার করি।
চেষ্টা করতে ~/testdir এখনো প্রেজেন্ট ওয়ার্কিং ডিরেক্টরি (PWD) কিনা নিশ্চিত করুন। ডিরেক্টরি ক্লিন করুন, তারপর এক্সপ্লিসিট ভ্যালু লিস্ট দিয়ে for লুপের একটি טרিভিয়াল উদাহরণ দেখুন। এই লিস্টটি অ্যালফানিউমেরিক ভ্যালুর মিশ্রণ—ভুলবেন না সব ভেরিয়েবল স্ট্রিং এবং স্ট্রিং হিসেবে বর্তাব করা যায়।
[student@studentvm1 testdir]$ rm * [student@studentvm1 testdir]$ for I in a b c d 1 2 3 4 ; do echo $I ; done a b c d 1 2 3 4
অর্থপূর্ণ ভেরিয়েবল নাম দিয়ে কিছুটা আরও ইউজফুল ভার্শন:
[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Department $Dept" ; done Department Human Resources Department Sales Department Finance Department Information Technology Department Engineering Department Administration Department Research
ডিরেক্টরি তৈরি করুন (প্রগ্রেস ইনফো সহ):
[student@studentvm1 testdir]$ for Dept in "Human Resources" Sales Finance "Information Technology" Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept" ; done Working on Department Human Resources Working on Department Sales Working on Department Finance Working on Department Information Technology Working on Department Engineering Working on Department Administration Working on Department Research [student@studentvm1 testdir]$ ll total 28 drwxrwxr-x 2 student student 4096 Apr 8 15:45 Administration drwxrwxr-x 2 student student 4096 Apr 8 15:45 Engineering drwxrwxr-x 2 student student 4096 Apr 8 15:45 Finance drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Human Resources' drwxrwxr-x 2 student student 4096 Apr 8 15:45 'Information Technology' drwxrwxr-x 2 student student 4096 Apr 8 15:45 Research drwxrwxr-x 2 student student 4096 Apr 8 15:45 Sales
$Dept ভেরিয়েবলটি mkdir স্টেটমেন্টে কোটেশনের ভিতরে রাখা আবশ্যক; অন্যথায় দুই শব্দের ডিপার্টমেন্ট নাম (যেমন "Information Technology") দুটি আলাদা ডিপার্টমেন্ট হিসেবে চিন्हিত হবে। এটি একটি বেস্ট প্র্যাকটিস হাইলাইট করে যা আমি মেনে চলি: সব ফাইল ও ডিরেক্টরির নাম একক শব্দ হওয়া উচিত। যদিও আধুনিক ওএস স্পেসসহ নাম হ্যান্ডল করতে পারে, তবে সিস্টেম অ্যাডমিনদের স্ক্রিপ্ট এবং CLI প্রোগ্রামে সেই স্পেশ্যাল কেসগুলো হ্যান্ডল করার জন্য একস্ট্রা পরিশ্রম করতে হয়। (সেগুলো অবশ্যই বিবেচনা করা উচিত, কष्टকর হলেও, কারণ আপনি কখনো জানেন না কী ফাইল পাবেন।)
তাহলে ~/testdir-এর সবকিছু ডিলিট করুন—আবার—and একবার আর করুন:
[student@studentvm1 testdir]$ rm -rf * ; ll total 0 [student@studentvm1 testdir]$ for Dept in Human-Resources Sales Finance Information-Technology Engineering Administration Research ; do echo "Working on Department $Dept" ; mkdir "$Dept" ; done Working on Department Human-Resources Working on Department Sales Working on Department Finance Working on Department Information-Technology Working on Department Engineering Working on Department Administration Working on Department Research [student@studentvm1 testdir]$ ll total 28 drwxrwxr-x 2 student student 4096 Apr 8 15:52 Administration drwxrwxr-x 2 student student 4096 Apr 8 15:52 Engineering drwxrwxr-x 2 student student 4096 Apr 8 15:52 Finance drwxrwxr-x 2 student student 4096 Apr 8 15:52 Human-Resources drwxrwxr-x 2 student student 4096 Apr 8 15:52 Information-Technology drwxrwxr-x 2 student student 4096 Apr 8 15:52 Research drwxrwxr-x 2 student student 4096 Apr 8 15:52 Sales
মান लीজিয়ে কেউ একটি লিনাক্স কম্পিউটারের সকল RPM এবং প্রতিটির সংক্ষিপ্ত বিবরণ চায়। আমার সাথে এটি ঘটেছিল নর্থ কэрোলিনা স্টেটে কাজ করার সময়। ওপেন সোর্স তখন স্টেট এজেন্সির দ্বারা "অনুমোদিত" নয়, এবং আমি শুধু ডেস্কটপে লিনাক্স ব্যবহার করতাম, তাই পয়েন্টি-হেয়ার্ড বসরা (PHBs) আমার কম্পিউটারে ইনস্টল করা প্রতিটি সফটওয়্যারের লিস্ট চাইল যাতে তারা "এক্সেপশন" অনুমোদন করতে পারে।
আপনি কীভাবে এপ্রোচ করবেন? একটা উপায়, যেনে যে rpm –qa কমান্ড একটি RPM-এর পূর্ণ বিবরণ দেয়, যার মধ্যে PHBs-র চাওয়া দুটি আইটেম রয়েছে: সফটওয়্যার নাম এবং সংক্ষিপ্ত সামারি।
ফাইনাল রেজাল্ট ধাপে ধাপে বিল্ড করি। প্রথমে সকল RPM লিস্ট করুন:
[student@studentvm1 testdir]$ rpm -qa perl-HTTP-Message-6.18-3.fc29.noarch perl-IO-1.39-427.fc29.x86_64 perl-Math-Complex-1.59-429.fc29.noarch lua-5.3.5-2.fc29.x86_64 java-11-openjdk-headless-11.0.ea.28-2.fc29.x86_64 util-linux-2.32.1-1.fc29.x86_64 libreport-fedora-2.9.7-1.fc29.x86_64 rpcbind-1.2.5-0.fc29.x86_64 libsss_sudo-2.0.0-5.fc29.x86_64 libfontenc-1.1.3-9.fc29.x86_64 <snip>
sort এবং uniq কমান্ড যোগ করুন লিস্ট সর্ট এবং ইউনিক প্রিন্ট করতে (কারণ একই নামের RPM একাধিকবার ইনস্টল থাকতে পারে):
[student@studentvm1 testdir]$ rpm -qa | sort | uniq a2ps-4.14-39.fc29.x86_64 aajohan-comfortaa-fonts-3.001-3.fc29.noarch abattis-cantarell-fonts-0.111-1.fc29.noarch abiword-3.0.2-13.fc29.x86_64 abrt-2.11.0-1.fc29.x86_64 abrt-addon-ccpp-2.11.0-1.fc29.x86_64 abrt-addon-coredump-helper-2.11.0-1.fc29.x86_64 abrt-addon-kerneloops-2.11.0-1.fc29.x86_64 abrt-addon-pstoreoops-2.11.0-1.fc29.x86_64 abrt-addon-vmcore-2.11.0-1.fc29.x86_64 <snip>
যেহেতু এটি RPM-এর সঠিক লিস্ট দিচ্ছে, এটাকে লুপের ইনপুট লিস্ট হিসেবে ব্যবহার করি যা প্রতিটি RPM-এর ডিটেইল প্রিন্ট করবে:
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done
এই কোড আপনার চাওয়া থেকে অনেক বেশি ডেটা দেবে। লুপটি সম্পন্ন। পরবর্তী ধাপ: PHBs-র চাওয়া ইনফরমেশন মাত্র এক্সট্র্যাক্ট করুন। সেজন্য egrep কমান্ড যোগ করুন, যা ^Name বা ^Summary সিলেক্ট করে। ক্যারেট (^) লাইনের শুরু নির্দেশ করে; যেনে যে লাইনে Name বা Summary লাইনের শুরুতে থাকে, সেগুলো দেখাবে।
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary" Name : a2ps Summary : Converts text and other types of files to PostScript Name : aajohan-comfortaa-fonts Summary : Modern style true type font Name : abattis-cantarell-fonts Summary : Humanist sans serif font Name : abiword Summary : Word processing program Name : abrt Summary : Automatic bug detection and reporting tool <snip>
উপরের কমান্ডে grep বসাতে egrep-এর বদলে চেষ্টা করুন, কিন্তু কাজ করবে না। আপনি আউটপুটকে less ফিল্টারের মাধ্যমে পাইপ করে রেজাল্ট এক্সপ্লোর করতে পারেন। ফাইনাল কমান্ড সিকোয়েন্স:
[student@studentvm1 testdir]$ for RPM in `rpm -qa | sort | uniq` ; do rpm -qi $RPM ; done | egrep -i "^Name|^Summary" > RPM-summary.txt
এই কমান্ড-লাইন প্রোগ্রামে পাইপলাইন, রিডাইরেকশন এবং for লুপ—সবকিছু এক লাইনে। এটি আপনার ছোট CLI প্রোগ্রামের আউটপুট একটি ফাইলে রিডাইরেক্ট করে যা ইমেইলে বা অন্যান্য উদ্দেশ্যে ইনপুট হিসেবে ব্যবহার করা যায়।
প্রোগ্রাম ধাপে ধাপে বিল্ড করার এই প্রক্রিয়া আপনাকে প্রতিটি ধাপের রেজাল্ট দেখতে এবং প্রত্যাশা অনুযায়ী কাজ করছে কিনা নিশ্চিত করতে দেয়।
এই এক্সারসাইজ থেকে PHBs-রা ১,৯০০-এরও বেশি RPM প্যাকেজের লিস্ট পেল। আমি সিরিয়াসলি ডাউট করি কেউ সেটি পড়েছে। কিন্তু আমি তাদের চাওয়া এক্স্যাক্টলি দিয়ে দিয়েছি, এবং সেটি নিয়ে আমার সাথে আর কোনো কথা বলল না।
অন্যান্য লুপ
Bash-এ আরও দুটি লুপ স্ট্রাকচার রয়েছে: while এবং until, যেগুলো সিন্ট্যাক্স এবং ফাংশন দুনো দিয়ে একে অপরের সাথে খুব সাদৃশ্যপূর্ণ। এই লুপ স্ট্রাকচারদের বেসিক সিন্ট্যাক্স সহজ:
while [ expression ] ; do list ; done
এবং
until [ expression ] ; do list ; done
প্রথমটির লজিক: "যতক্ষণ এক্সপ্রেশন true আঙ্কিত হয়, প্রোগ্রাম স্টেটমেন্টের লিস্ট এক্সিকিউট করুন। এক্সপ্রেশন false হলে লুপ থেকে বের হয়ে যান।" দ্বিতীয়টির: "যতক্ষণ এক্সপ্রেশন true হয় না, প্রোগ্রাম স্টেটমেন্টের লিস্ট এক্সিকিউট করুন। এক্সপ্রেশন true হলে লুপ থেকে বের হয়ে যান।"
While loop
while লুপ প্রোগ্রাম স্টেটমেন্টের একটি সিরিজ এক্সিকিউট করে যতক্ষণ লজিক্যাল এক্সপ্রেশন true আঙ্কিত হয়। আপনার PWD এখনো ~/testdir হওয়া উচিত।
while লুপের সিম্পলেস্ট ফর্ম হলো অনন্তকাল চলা লুপ। নিচের ফর্মে true স্টেটমেন্ট ব্যবহার করা হয়েছে যেটি সবসময় "true" রিটার্ন কোড জেনারেট করে। আপনি সিম্পল "১" ও ব্যবহার করতে পারতেন—সেটিও একই কাজ করত—কিন্তু এটি true স্টেটমেন্টের ব্যবহার ইলাস্ট্রেট করে:
[student@studentvm1 testdir]$ X=0 ; while [ true ] ; do echo $X ; X=$((X+1)) ; done | head 0 1 2 3 4 5 6 7 8 9 [student@studentvm1 testdir]$
এই CLI প্রোগ্রাম এখন আরও বোঝা যাচ্ছে কারণ আপনি এর অংশগুলো পড়েছেন। প্রথমে $X কে শূন্যে সেট করা হয়েছে কোনো পূর্ববর্তী প্রোগ্রাম বা CLI কমান্ড থেকে বাকি থাকা ভ্যালু না থাকার জন্য। তারপর, যেহেতু লজিক্যাল এক্সপ্রেশন [ true ] সবসময় ১ (true) আঙ্কিত হয়, do এবং done-এর মাঝপথে থাকা প্রোগ্রাম ইনস্ট্রাকশনগুলো অনন্তকাল এক্সিকিউট হবে—অথবা আপনি Ctrl+C প্রেস না করুন বা অন্যভাবে সিগন্যাল ২ না পাঠান। ইনস্ট্রাকশনগুলো হলো একটি অ্যারিথমেটিক এক্সপ্যানশন যা $X-র বর্তমান ভ্যালু প্রিন্ট করে এবং তারপর ১ দিয়ে ইনক্রিমেন্ট করে।
The Linux Philosophy for Sysadmins-র দশটি তত্বের মধ্যে একটি হলো এলিগেন্সের জন্য চেষ্টা করা, এবং সাদگی এলিগেন্স অর্জনের একটি উপায়। আপনি ভেরিয়েবল ইনক্রিমেন্ট অপারেটর, ++ ব্যবহার করে এই প্রোগ্রামকে সিম্পলিফাই করতে পারেন। প্রথম ইন্সট্যান্সে, ভেরিয়েবলের বর্তমান ভ্যালু প্রিন্ট হয় এবং তারপর ভেরিয়েবল ইনক্রিমেন্ট হয়। এটি ভেরিয়েবলের পরে ++ অপারেটর রেখে নির্দেশ করা হয়:
[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((X++)) ; done | head 0 1 2 3 4 5 6 7 8 9
এখন প্রোগ্রামের শেষ থেকে | head মুছে আবার চালান।
এই ভার্শনে, ভেরিয়েবল প্রথমে ইনক্রিমেন্ট হয় তারপর এর ভ্যালু প্রিন্ট হয়। এটি ভেরিয়েবলের আগে ++ অপারেটর রেখে নির্দেশ করা হয়। পার্থক্য দেখছেন?
[student@studentvm1 ~]$ X=0 ; while [ true ] ; do echo $((++X)) ; done | head 1 2 3 4 5 6 7 8 9
আপনি দুটি স্টেটমেন্ট একটিতে কমিয়ে দিয়েছেন যা ভেরিয়েবলের ভ্যালু প্রিন্ট করে এবং সেটি ইনক্রিমেন্ট করে। একটি ডিক্রিমেন্ট অপারেটরও রয়েছে, --।
লুপটি নির্দিষ্ট সংখ্যায় থামানোর একটি মেথড দরকার। সেজন্য true এক্সপ্রেশনকে একটি একচুয়েটিক নিউমেরিক ইভ্যালুয়েশন এক্সপ্রেশনতে পরিবর্তন করুন। প্রোগ্রাম ৫ পর্যন্ত লুপ করে থামাবে। নিচের উদাহরণ কোডে দেখুন -le হলো "less than or equal to" এর জন্য লজিক্যাল নিউমেরিক অপারেটর। এতে মানে: "যতক্ষণ $X ৫-এর চেয়ে কম বা সমান, লুপ চলবে। $X ৬ হলে লুপ টার্মিনেট হবে।"
[student@studentvm1 ~]$ X=0 ; while [ $X -le 5 ] ; do echo $((X++)) ; done 0 1 2 3 4 5 [student@studentvm1 ~]$
Until loop
until কমান্ড খুব বেশি while কমান্ডের মতো। পার্থক্য হলো এটি লুপ চালাতে থাকবে যতক্ষণ না লজিক্যাল এক্সপ্রেশন "true" আঙ্কিত হয়। এই কনস্ট্রাক্টের সিম্পলেস্ট ফর্ম দেখুন:
[student@studentvm1 ~]$ X=0 ; until false ; do echo $((X++)) ; done | head 0 1 2 3 4 5 6 7 8 9 [student@studentvm1 ~]$
এটি একটি লজিক্যাল কম্পেরিজন দিয়ে নির্দিষ্ট ভ্যালুতে কাউন্ট করে:
[student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo $((X++)) ; done 0 1 2 3 4 [student@studentvm1 ~]$ X=0 ; until [ $X -eq 5 ] ; do echo $((++X)) ; done 1 2 3 4 5 [student@studentvm1 ~]$
সারসংক্ষেপ
এই সিরিজে Bash কমান্ড-লাইন প্রোগ্রাম এবং শেল স্ক্রিপ্ট বিল্ড করার অনেক শক্তিশালী টুল আলোচনা করা হয়েছে। কিন্তু এটি Bash-র সাথে যা যা করা যায় তার поверхামাত্রো ছুঁয়েছে; বাকি আপনার ওপর।
আমি খুঁজে পেয়েছি Bash প্রোগ্রামিং শিখার সেরা উপায় হলো এটি করাই। একটি সিম্পল প্রজেক্ট খুঁজুন যেটিতে একাধিক Bash কমান্ড লাগে এবং সেটাকে একটি CLI প্রোগ্রাম বানান। সিস্টেম অ্যাডমিনরা অনেক টাস্ক করেন যা CLI প্রোগ্রামিংয়ে ফিট করে, তাই আপনি সহজে অটোমেট করার টাস্ক পাবেন।
অনেক বছর আগে, অন্যান্য শেল ল্যাঙ্গুয়েজ এবং Perl জানা সত্ত্বেও, আমি আমার সকল সিস্টেম অ্যাডমিন অটোমেশন টাস্কের জন্য Bash ব্যবহার করার সিদ্ধান্ত নিয়েছিলাম। আমি আবিষ্কার করেছি যে—কখনো কখনো একটু সার্চ করলে—Bash দিয়ে আমার প্রয়োজনীয় সবকিছু করতে পেরেছি।