শেল হলো এমন একটি প্রোগ্রাম, যা দিয়ে আপনি কমান্ড লিখে অপারেটিং সিস্টেমের সাথে যোগাযোগ করতে পারেন। শেল গ্রাফিকালও হতে পারে; মাউস বা ট্র্যাকপ্যাড দিয়ে আপনি যে ডেস্কটপ পরিবেশ ব্যবহার করেন, সেটিও একটি শেল। আমরা এখানে মূলত কমান্ড-লাইন ইন্টারফেস নিয়ে আলোচনা করব, যেখানে আপনি কমান্ড টাইপ করেন আর ফলাফল (সাধারণত) আপনার টার্মিনালে ছাপা হয়।
ভিন্ন ভিন্ন কমান্ড-লাইন শেল আছে। Windows-এ আছে cmd আর powershell। Mac আর Linux-এ পাওয়া যায় "Unix শেল", যার অনেক রকমভেদ আছে। Mac-এর ডিফল্ট শেল হলো zsh। csh হলো C-এর মতো সিনট্যাক্সওয়ালা একটি পুরনো শেল। bash সবচেয়ে জনপ্রিয় শেলগুলোর একটি, বিশেষ করে Linux-এ।
সবচেয়ে প্রথম দিকের শেলগুলোর একটি হলো Bourne শেল, sh। সময়ের সাথে এই শেলটি স্ট্যান্ডার্ডায়িত হয়, যাতে প্রতিটি Unix-সদৃশ সিস্টেমে এটি দেওয়া থাকে। অনেক শেলই Bourne শেলের উপর ভিত্তি করে তৈরি, যেগুলো প্রায়ই বাড়তি ইন্টার্যাক্টিভ বা প্রোগ্রামিং সুবিধা দেয়।
Bash তৈরি হয়েছে Bourne শেলের উপর ভিত্তি করে। এজন্যই এর নাম "Bourne-again shell"। এটি অনেক Linux সিস্টেমের ডিফল্ট শেল। Bash অনেক সুবিধা দেয়, যেগুলো এটিকে একটি আনন্দদায়ক ইন্টার্যাক্টিভ শেল করে তোলে, যেমন:
এই ট্র্যাকটি ইন্টার্যাক্টিভ শেল হিসেবে bash ব্যবহার করা নিয়ে নয়। এটি হলো bash দিয়ে প্রোগ্রাম লেখা শেখা নিয়ে। সমস্যা সমাধানের জন্য শুধু bash ব্যবহারের উপর জোর দেওয়া হবে, যদিও বাইরের টুল কল করার উপর কোনো নিষেধাজ্ঞা নেই।
Bash তার ইনপুট থেকে কমান্ড পড়ে (যা সাধারণত হয় একটি টার্মিনাল, নয়তো একটি ফাইল)। সে যে ইনপুট লাইন পড়ে, প্রতিটি লাইনকেই একটি কমান্ড হিসেবে ধরা হয়, অর্থাৎ পালন করার একটি নির্দেশ। (কয়েকটি অ্যাডভান্সড ক্ষেত্র আছে, যেমন একাধিক লাইনে ছড়ানো কমান্ড, সেগুলো পরে আলোচনা করা হবে।)
Bash প্রতিটি লাইনকে ওয়ার্ড-এ ভাগ করে, যেগুলো হোয়াইটস্পেস ক্যারেক্টার (স্পেস ও ট্যাব) দিয়ে আলাদা করা থাকে। লাইনের প্রথম ওয়ার্ডটি হলো যে কমান্ড চালানো হবে তার নাম। বাকি সব ওয়ার্ড সেই কমান্ডের আর্গুমেন্ট হয়ে যায় (অপশন, ফাইলের নাম ইত্যাদি)।
নিচের উদাহরণগুলোতে আমরা কিছু ইন্টার্যাক্টিভ কমান্ড দেখাব, যেগুলো আপনি চালাতে পারেন।
# ক্যারেক্টার দিয়ে একটি কমেন্ট শুরু হয়।
কমেন্টের পরের যেকোনো শব্দ শেল উপেক্ষা করে; কমেন্ট শুধু পড়ার জন্যই লেখা হয়।$ ক্যারেক্টার একটি শেল প্রম্পট বোঝায়।
কমান্ড কপি করলে প্রম্পটটি কপি করবেন না।# Note: It is always a good idea to run `ls` before `rm`
# to make sure you know what you are deleting.
$ rm * # Remove all files in the current directory.
# Make sure you run this in a temp directory or you can lose files!
$ ls # List files in the current directory (no output, no files).
$ touch a b c # Create files 'a', 'b' and 'c'.
$ ls # List files again; this time the outputs: 'a', 'b' and 'c'.
a b c
rm হলো একটি অ্যাপ্লিকেশন, যা তাকে দেওয়া ফাইলগুলো মুছে ফেলে।
* হলো একটি glob ক্যারেক্টার, একটি "ওয়াইল্ডকার্ড" প্যাটার্ন, যা বর্তমান ডিরেক্টরির সব ফাইলের সাথে মিলে যায়।
glob নিয়ে আমরা পরে আরও কথা বলব।
লক্ষ্য করলেন কি, a আর b-এর মাঝে বেশ কয়েকটি স্পেস আছে, অথচ b আর c-এর মাঝে মাত্র একটি?
আর্গুমেন্টের মাঝে কতটুকু হোয়াইটস্পেস আছে, তা কোনো ব্যাপারই নয়!
যেমন:
$ echo This is a test.
This is a test.
$ echo This is a test.
This is a test.
echo হলো এমন একটি কমান্ড, যা তার আর্গুমেন্টগুলো স্ট্যান্ডার্ড আউটপুটে ছাপে (আমাদের ক্ষেত্রে যা টার্মিনাল)।
Bash কমান্ডটিকে পাঁচটি ওয়ার্ডে ভাগ করে আর echo কমান্ডটি চারটি আর্গুমেন্ট নিয়ে কল করে।
দ্বিতীয় উদাহরণের বাড়তি স্পেসগুলো কোনো পার্থক্য তৈরি করে না।
আগে ওয়ার্ডগুলোর মাঝে স্পেসগুলো কেমন ছিল, তা echo জানে না।
বাড়তি হোয়াইটস্পেস ধরে রাখতে চাইলে বাক্যটিকে একটি একক আর্গুমেন্ট হিসেবে পাস করতে হবে। কোট ব্যবহার করে আমরা এটি করতে পারি:
$ echo "This is a test."
This is a test.
কোট তার ভেতরের সবকিছুকে একটি একক আর্গুমেন্ট-এ পরিণত করে।
আর্গুমেন্টটি হলো This is a test., যেখানে ওই নির্দিষ্ট স্পেস ক্যারেক্টারগুলোও আছে।
খেয়াল করুন, কোটগুলো আর্গুমেন্টের অংশ নয়; একটি একক আর্গুমেন্ট echo-কে দেওয়ার আগে bash সেগুলো সরিয়ে ফেলে।
কমান্ডের নাম থেকে এবং একে অপরের থেকে আর্গুমেন্টগুলো হোয়াইটস্পেস দিয়ে আলাদা করা হয়। এটা মনে রাখা জরুরি। যেমন, নিচেরটি ভুল:
$ [-f file]
bash: [-f: command not found
এটি দিয়ে file নামের একটি ফাইল আছে কি না, তা পরীক্ষা করার কথা বোঝানো হয়েছে।
ধরে নেওয়া যুক্তিসঙ্গত যে [ আর ]-এর আশপাশের হোয়াইটস্পেস কোনো ব্যাপার নয়, কিন্তু আসলে [ একটি কমান্ড, আর এর শেষ আর্গুমেন্টটি ] হতে হবে।
(আমরা [ কমান্ডটি পরে আরও বিস্তারিতভাবে আলোচনা করব।)
তাই [-কে -f থেকে আর ]-কে file থেকে আলাদা করতে হবে।
নাহলে bash মনে করবে, আমরা file] নামের একটি একক আর্গুমেন্ট দিয়ে [-f নামের একটি কমান্ড চালাতে চাইছি।
সঠিক কমান্ডে সব আর্গুমেন্ট হোয়াইটস্পেস দিয়ে আলাদা করা থাকে:
$ [ -f file ]
(আমরা অনেককে দেখেছি যাঁরা এই আচরণে বিভ্রান্ত হন; তাঁরা ভাবেন [...] হলো "শুধুই সিনট্যাক্স" আর স্পেসগুলো বাদ দেওয়া যায়; কিন্তু [ একটি কমান্ড, আর হোয়াইটস্পেসটা থাকতেই হবে।)
আমাদের ফাইলের নামে হোয়াইটস্পেস বা অন্য কোনো স্পেশাল ক্যারেক্টার থাকলে সেটিও কোট করতে হবে:
$ [ -f "my file" ]
Bash বিভিন্ন ধরনের কমান্ড বোঝে: অ্যালিয়াস, ফাংশন, বিল্টইন, কিওয়ার্ড আর এক্সিকিউটেবল। এগুলো সবই কমান্ড, আর সবগুলোই প্রায় একইভাবে কাজ করে।
ডিফল্টভাবে অ্যালিয়াস শুধু ইন্টার্যাক্টিভ শেলে চালু থাকে, শেল স্ক্রিপ্টে নয়। সেগুলো এখানে আলোচনা করব না।
একটি ফাংশনে শেল কমান্ড থাকে, আর এটি অনেকটা ছোট একটি স্ক্রিপ্টের মতো কাজ করে; এমনকি এরা আর্গুমেন্ট নিতে পারে আর লোকাল ভ্যারিয়েবলও তৈরি করতে পারে। কোনো ফাংশন কল করা হলে তার ভেতরের কমান্ডগুলো চালানো হয়। ফাংশন নিয়ে পরে বিস্তারিত আলোচনা করা হবে।
Bash-এ কিছু মৌলিক কমান্ড তৈরি করা অবস্থায় থাকে, যেমন: cd (ডিরেক্টরি পরিবর্তন), echo (আউটপুট লেখা) ইত্যাদি।
এগুলোকে আগে থেকেই দেওয়া ফাংশন হিসেবে ভাবা যায়।
কিওয়ার্ড বিল্টইনের মতোই, মূল পার্থক্য হলো কিওয়ার্ড আসলে bash সিনট্যাক্স, আর বিশেষ নিয়মে এগুলো পার্স করা হতে পারে।
যেমন, [ একটি bash বিল্টইন, আর [[ একটি bash কিওয়ার্ড; দুটোই নানা শর্ত পরীক্ষা করতে ব্যবহৃত হয়।
কিন্তু [[ কিওয়ার্ডে কিছু বাড়তি সুবিধা আছে।
যেমন, এটি =~ অপারেটর দেয়, যা রেগুলার এক্সপ্রেশন দিয়ে পরীক্ষা করে।
$ [[ "hello world" =~ e.*o ]] # returns a "success" status
$ [ "hello world" =~ e.*o ]
bash: [: =~: binary operator expected
কিওয়ার্ড কীভাবে কোড ভিন্নভাবে পার্স করে, তার আরেকটি উদাহরণ হলো স্ট্রিং তুলনার অপারেটর < আর >।
[[...]]-এর ভেতরে এই অপারেটরগুলোর জন্য বিশেষ কোনো সতর্কতার দরকার নেই।
$ if [[ "$USER" < "n" ]]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
কিন্তু [...]-এর ভেতরে শেল যাতে এগুলোকে রিডিরেকশন অপারেটর হিসেবে পার্স না করে, সেজন্য এগুলো কোট বা এস্কেপ করতে হবে।
$ if [ "$USER" < "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => bash: n: No such file or directory
$ if [ "$USER" \< "n" ]; then
echo "$USER is in the first half of the alphabet"
fi
# => glennj is in the first half of the alphabet
bash দিয়ে চালানো যায় এমন শেষ ধরনের কমান্ডটি হলো এক্সিকিউটেবল।
এগুলো হলো আপনার কম্পিউটারের ফাইলসিস্টেমে থাকা এক্সিকিউটেবল ফাইল (প্রোগ্রাম), যেগুলো আপনি চালাতে পারেন।
(এক্সিকিউটেবলকে এক্সটার্নাল কমান্ড বা অ্যাপ্লিকেশন-ও বলা হয়।)
সাধারণত শুধু নাম টাইপ করেই এক্সিকিউটেবল কল করা হয়; ফাইলের সম্পূর্ণ পাথ সাধারণত দরকার হয় না।
এটা সম্ভব কারণ bash একটি পূর্বনির্ধারিত ভ্যারিয়েবল ব্যবহার করে, যা সাধারণ এক্সিকিউটেবল ফাইলের অবস্থান খুঁজে পেতে সাহায্য করে।
এই ভ্যারিয়েবলের নাম PATH।
এটি কোলন দিয়ে আলাদা করা ডিরেক্টরির নামের একটি সেট (যেমন /usr/bin:/bin)।
কোনো কমান্ড ফাইলের পাথ ছাড়া উল্লেখ করা হলে (যেমন myprogram বা ls), আর তা অ্যালিয়াস, ফাংশন, বিল্টইন বা কিওয়ার্ড না হলে, bash PATH-এর ডিরেক্টরিগুলোতে খোঁজে।
ডিরেক্টরিগুলো বাম থেকে ডানে ক্রমানুসারে খোঁজা হয়, দেখা হয় সেগুলোতে প্রদত্ত নামের কোনো এক্সিকিউটেবল আছে কি না।
এক্সিকিউটেবলটি যদি পরিচিত কোনো পাথের বাইরে থাকে, তবে এক্সিকিউটেবলের ফাইল পাথ উল্লেখ করতে হবে।
বর্তমান ডিরেক্টরিতে থাকা myprogram এক্সিকিউটেবলের জন্য ./myprogram ব্যবহার করুন; এটি যদি /opt/somedirectory ডিরেক্টরিতে থাকে, তবে /opt/somedirectory/myprogram ব্যবহার করুন।
টিপ: কমান্ড সম্পর্কে বিস্তারিত জানতে type কমান্ড ব্যবহার করা যায়:
$ type rm
rm is hashed (/bin/rm)
# or it might return: `rm is /bin/rm`
$ type cd
cd is a shell builtin
স্ক্রিপ্ট হলো একটি ফাইলে রাখা কমান্ডের ক্রম। Bash ফাইলটি পড়ে আর কমান্ডগুলো ক্রমানুসারে প্রসেস করে। বর্তমান কমান্ড শেষ হলেই এটি পরের কমান্ডে যায়। ব্যতিক্রম হলো, কোনো কমান্ড যদি অ্যাসিনক্রোনাসভাবে (ব্যাকগ্রাউন্ডে) চালানোর জন্য বলা হয়ে থাকে। এই ক্ষেত্রটি নিয়ে এখন খুব বেশি চিন্তা করবেন না; এটা কীভাবে কাজ করে, তা আমরা পরে শিখব।
এই গাইডের যেকোনো কমান্ড-লাইন উদাহরণ স্ক্রিপ্টেও ব্যবহার করা যায়।
স্ক্রিপ্ট তৈরি করা সহজ। শুরু করুন একটি নতুন ফাইল তৈরি করে, আর প্রথম লাইনে এটা লিখুন:
#!/bin/bash
এই লাইনটিকে ইন্টারপ্রেটার ডিরেক্টিভ বলা হয় (এটিকে হ্যাশব্যং বা শেব্যাং-ও বলা হয়)।
এটি বলে দেয়, কমান্ডে ফাইলটিকে এক্সিকিউটেবল হিসেবে ব্যবহার করলে /bin/bash-কে ইন্টারপ্রেটার হিসেবে ব্যবহার করতে হবে।
OS-এর কার্নেল কোনো নন-বাইনারি ফাইল চালালে সেটি ফাইলের প্রথম লাইন পড়ে।
লাইনটি #! দিয়ে শুরু হলে কার্নেল ওই লাইন দেখে ঠিক করে, ফাইলটি কোন ইন্টারপ্রেটারের কাছে পাঠাতে হবে।
(এটি করার আরও বৈধ উপায় আছে, নিচে দেখুন।)
#! ফাইলের একেবারে শুরুতে থাকতে হবে, তার আগে কোনো স্পেস বা খালি লাইন থাকবে না।
আমাদের স্ক্রিপ্টের কমান্ডগুলো এর নিচে আলাদা আলাদা লাইনে থাকবে।
টিপ: #!/bin/bash-এর বদলে আপনি ব্যবহার করতে পারেন
#!/usr/bin/env bash
env তার প্রথম আর্গুমেন্টে বলা এক্সিকিউটেবলটি (এক্ষেত্রে "bash") খুঁজতে $PATH-এ সার্চ করে।
এই কৌশলটির আরও বিস্তারিত ব্যাখ্যা এবং সাধারণ #!/bin/bash-এর সাথে এর পার্থক্য জানতে StackOverflow-এর এই প্রশ্নটি দেখুন।
ইন্টারনেটে /bin/sh-কে ইন্টারপ্রেটার হিসেবে ব্যবহার করা স্ক্রিপ্ট বা উদাহরণ দেখে বিভ্রান্ত হবেন না।
sh আর bash এক নয়!
Bash নিজেই একটি "sh-সামঞ্জস্যপূর্ণ" শেল, অর্থাৎ এটি বেশিরভাগ sh স্ক্রিপ্ট চালাতে পারে আর এর অনেক সিনট্যাক্সও একই রকম।
তবে উল্টোটা সত্য নয়; bash-এর কিছু ফিচার sh-এ ভেঙে যাবে বা অপ্রত্যাশিত আচরণ করবে।
আসলে কোনো একক sh শেল বলে কিছু নেই।
POSIX-sh সিনট্যাক্স সমর্থন করে এমন অনেক শেল ইমপ্লিমেন্টেশন আছে।
আপনার OS-এর উপর নির্ভর করে, sh চালালে আপনি আসলে bash, ash, dash বা অন্য কোনো শেল চালাতে পারেন।
স্ক্রিপ্ট ফাইল তৈরি হয়ে গেলে এটি এভাবে চালানো যায়:
$ bash myscript
এই উদাহরণে আমরা bash কমান্ডটি চালাই আর তাকে বলি, আমরা আর্গুমেন্ট হিসেবে যে স্ক্রিপ্ট দিয়েছি সেটি পড়তে।
এভাবে করলে #! লাইনটি শুধুই একটি কমেন্ট।
বিকল্পভাবে, আমরা আমাদের স্ক্রিপ্টকে এক্সিকিউটেবল পারমিশন দিতে পারি। এই পদ্ধতিতে হাতে bash কল না করে যেকোনো এক্সিকিউটেবলের মতোই স্ক্রিপ্টটি চালানো যায়:
$ chmod +x myscript # Mark the file as executable.
$ ./myscript # Now, myscript can be executed directly
# instead of having to specify bash.
এভাবে কমান্ড চালালে অপারেটিং সিস্টেম (OS) #! লাইন দেখে ঠিক করে, আপনার স্ক্রিপ্ট চালাতে কোন ইন্টারপ্রেটার ব্যবহার করবে।
এই পাঠের বিষয়বস্তু নেওয়া হয়েছে Bash Guide-এর Commands and Arguments অংশ থেকে।