ট্র্যাক
/
Bash
Bash
/
সিলেবাস
/
কোয়োটিংয়ের গুরুত্ব
কো

কোয়োটিংয়ের গুরুত্ব মধ্যে Bash

{one: "১টি অনুশীলনী", other: "%{count}টি অনুশীলনী"}

কোয়োটিংয়ের গুরুত্ব সম্পর্কে

নতুন bash প্রোগ্রামাররা বারবার যে একটি পরামর্শ শুনবেন:

আপনার ভ্যারিয়েবল কোট করুন!

কেন এমন?

আপনি যখন কোনো ভ্যারিয়েবল (যেমন $name) এক্সপ্যান্ড করেন, তখন প্রায় সবসময়ই আপনি চান ভ্যারিয়েবলের মান একটি একক স্ট্রিং (বা সংখ্যা) হিসেবে ব্যবহৃত হোক। আপনি চান না যে মানটি অনাকাঙ্ক্ষিতভাবে দুই বা তার বেশি আলাদা স্ট্রিং হিসেবে ব্যবহার হয়ে যায়।

কোটিং

কোনো লেখার অংশকে একটি একক শব্দ হিসেবে গণ্য করতে কোটিং হলো প্রধান পদ্ধতি। কোট প্রধানত দুই ধরনের হয়।

ডাবল কোট

ডাবল কোটের ভেতরে প্যারামিটার এক্সপেনশন, অ্যারিথমেটিক এক্সপেনশন আর কমান্ড সাবস্টিটিউশন কাজ করে।

echo "This is bash version $BASH_VERSION"

ডাবল কোট দেওয়া স্ট্রিংয়ের ভেতরে একটি আক্ষরিক ডাবল কোট রাখতে চাইলে তার আগে একটি ব্যাকস্ল্যাশ বসান।

echo "She said \"Let me teach you about bash.\""

সিঙ্গেল কোট

সিঙ্গেল কোটের ভেতরে কোনো এক্সপেনশন কাজ করে না।

echo 'This is bash version $BASH_VERSION'
# ...^..................................^
# prints exactly: This is bash version $BASH_VERSION

সিঙ্গেল কোট দেওয়া স্ট্রিংয়ের ভেতরে একটি সিঙ্গেল কোট ঢোকানো সম্ভব নয়।

কোটিংয়ের গুরুত্ব

echo $name-এর আসলে কী সমস্যা?

শেল যখন কোনো কোডের লাইন পার্স করে, তখন সে ধাপে ধাপে কয়েক ধরনের এক্সপেনশন চালায়। শেষের দিকের দুটি এক্সপেনশন হলো ওয়ার্ড স্প্লিটিং আর ফাইলনেম এক্সপেনশন।

ওয়ার্ড স্প্লিটিং

প্যারামিটার এক্সপেনশন, কমান্ড সাবস্টিটিউশন বা অ্যারিথমেটিক এক্সপেনশনের যেসব ফলাফল ডাবল কোটের ভেতরে তৈরি হয়নি, সেগুলোই "ওয়ার্ড স্প্লিটিং"-এর জন্য বিবেচিত হয়। শেল এক্সপ্যান্ড হওয়া ফলাফলটি নেয়, তারপর $IFS-এর মানে থাকা ক্যারেক্টারের যেকোনো ধারাবাহিকতায় সেটিকে ভাগ করে। ডিফল্টভাবে এটি হলো স্পেস, ট্যাব আর নিউলাইন।

একটি শেল লুপ ব্যবহার করে এটি কাজ করতে দেখা যায়:

sentence="The quick brown fox jumps."
i=0
for word in "$sentence"; do echo "$((++i)) $word"; done
# ..........^.........^

আউটপুট দেয়

1 The quick brown fox jumps.

কিন্তু কোট ছাড়া:

i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

আউটপুট দেয়

1 The
2 quick
3 brown
4 fox
5 jumps.

চলুন $IFS-এর একটি ভিন্ন মান দিয়ে এটি দেখি:

csv="first,second,third"
IFS=","
for word in $csv; do echo "$word"; done

আউটপুট দেয়

first
second
third
ওয়ার্ড স্প্লিটিং বন্ধ করা

ওয়ার্ড স্প্লিটিং বন্ধ করার একটি কৌশল হলো $IFS-কে একটি খালি স্ট্রিংয়ে সেট করা

sentence="The quick brown fox jumps."
IFS=    # or explicitly, IFS=""
i=0
for word in $sentence; do echo "$((++i)) $word"; done
# .........^.........^

আউটপুট দেয়

1 The quick brown fox jumps.

তবে, ফাইলনেম এক্সপেনশনও বন্ধ না করলে ভ্যারিয়েবল কোট ছাড়া রেখে দেওয়া ঠিক নয়। সেটি কীভাবে করতে হয়, নিচে দেখব।

ফাইলনেম এক্সপেনশন

"গ্লোব" প্যাটার্ন দিয়ে একগুচ্ছ ফাইলের সাথে মেলানো যায় এমন সংক্ষিপ্ত প্যাটার্ন প্রকাশ করা হয়। উদাহরণস্বরূপ, আপনি একটি ডিরেক্টরির সব Markdown ফাইল *.md দিয়ে মেলাতে পারেন। ইউনিক্স আবিষ্কারের পর থেকেই গ্লোব প্যাটার্ন চলে আসছে।

গ্লোবের ওয়াইল্ডকার্ড ক্যারেক্টারগুলো হলো:

  • * যেকোনো ক্যারেক্টারের শূন্য বা তার বেশি অক্ষরের সাথে মেলে,

  • ? যেকোনো ক্যারেক্টারের ঠিক একটির সাথে মেলে,

  • [...] বন্ধনীর ভেতরে তালিকাভুক্ত ক্যারেক্টারের সেট থেকে ঠিক একটি ক্যারেক্টারের সাথে মেলে।

    Advanced
    ক্যারেক্টার সেট নিয়ে অনেক বিস্তারিত আছে। সেগুলো দেখতে এখানে ক্লিক করুন।
    • [abc] ঠিক একটি ক্যারেক্টারের সাথে মেলে, যেটি a বা b বা c যেকোনো একটি।
      • এক্সপ্রেশনে একটি আক্ষরিক ক্লোজ ব্র্যাকেট রাখতে চাইলে এটিকে ওপেনিং ব্র্যাকেটের পরে প্রথম ক্যারেক্টার করুন (ঐচ্ছিক ^-এর পরে)
        • [][] হলো এমন একটি ক্যারেক্টার সেট যা ক্লোজ কিংবা ওপেন ব্র্যাকেট দুটির যেকোনোটির সাথে মেলে।
        • অথবা ব্যাকস্ল্যাশ দিয়ে এস্কেপ করুন: [[\]]
    • [x-y] ঠিক একটি ক্যারেক্টারের সাথে মেলে, যেটি x থেকে y পর্যন্ত রেঞ্জের ভেতরে থাকে
      • একটি ক্যারেক্টার সেটে আক্ষরিক হাইফেন ক্যারেক্টার রাখতে চাইলে এটিকে হয় ওপেনিং ব্র্যাকেটের পরে প্রথম ক্যারেক্টার (ঐচ্ছিক ^-এর পরে), নয়তো ক্লোজিং ব্র্যাকেটের আগে শেষ ক্যারেক্টার হতে হবে।
        • [-xy] বা [xy-] প্রত্যেকটি ঠিক একটি ক্যারেক্টারের সাথে মেলে, যেটি - বা x বা y যেকোনো একটি
        • অথবা ব্যাকস্ল্যাশ দিয়ে এস্কেপ করুন: [x\-y]
      • একাধিক রেঞ্জ নির্দিষ্ট করা যায়, উদাহরণস্বরূপ [0-9A-Fa-f] একটি হেক্সাডেসিমাল ডিজিটের সাথে মেলে
    • [^abc] ঠিক একটি ক্যারেক্টারের সাথে মেলে, যেটি a বা b বা c নয়,
    • [^x-y] ঠিক একটি ক্যারেক্টারের সাথে মেলে, যেটি x থেকে y পর্যন্ত রেঞ্জের ভেতরে নয়
    • [[:character_class:]] একটি ক্যারেক্টারের সাথে মেলে, যেটি নাম দেওয়া "character_class"-এর ভেতরে আছে
      • পাওয়া যাওয়া ক্যারেক্টার ক্লাসগুলো হলো:
        • lower -- ছোট হাতের অক্ষর
        • upper -- বড় হাতের অক্ষর
        • alpha -- অক্ষর
        • digit -- ডিজিট
        • alnum -- অক্ষর আর ডিজিট
        • space -- হোয়াইটস্পেস
        • blank -- অনুভূমিক হোয়াইটস্পেস (স্পেস আর ট্যাব)
        • xdigit -- হেক্সাডেসিমাল ডিজিট
        • word -- আইডেন্টিফায়ারে অনুমোদিত ক্যারেক্টার (অক্ষর, সংখ্যা, আন্ডারস্কোর)
        • punct -- যতিচিহ্ন
        • cntrl -- কন্ট্রোল ক্যারেক্টার
        • graph -- "দৃশ্যমান" ক্যারেক্টার
        • print -- দৃশ্যমান ক্যারেক্টার আর স্পেস
      • এগুলো একসাথে ব্যবহার করা যায়:
        • [[:alpha:]] ক্লাসটি [[:lower:][:upper:]] নিয়ে গঠিত
        • [[:alnum:]] ক্লাসটি [[:alpha:][:digit:]] নিয়ে গঠিত
        • [[:word:]] ক্লাসটি [[:alnum:]_] নিয়ে গঠিত
        • আন্ডারস্কোর (অক্টাল 137) একইসাথে [[:word:]] আর [[:punct:]]
        • [[:graph:]] ক্লাসটি [[:alnum:][:punct:]] নিয়ে গঠিত
        • [[:print:]] ক্লাসটি [[:graph:] ] নিয়ে গঠিত -- শুধু স্পেস (অক্টাল 040), অন্য কোনো হোয়াইটস্পেস নয়।
ফাইলের তালিকার উপর ইটারেশন করা

আপনি একটি for লুপ দিয়ে ফাইলের তালিকার উপর ইটারেশন করতে পারেন

for file in *.csv; do
    do_something_with "$file"
done

লক্ষ্য করুন, "$file" ভ্যারিয়েবলটি কোট করা হয়েছে। গ্লোব প্যাটার্ন এমন ফাইলও ফেরত দিতে পারে যাদের নামে স্পেস আছে। আমাদের ভ্যারিয়েবলটি কোট করতে হবে, যাতে do_something_with কমান্ডটি ঠিক একটি আর্গুমেন্ট, অর্থাৎ ফাইলের নামটি পায়।

গ্লোব প্যাটার্ন রেগুলার এক্সপ্রেশন নয়

গ্লোব প্যাটার্ন রেগুলার এক্সপ্রেশন নয়।

  • ঠিক একটি ক্যারেক্টারের সাথে মেলে
    • রেজেক্স .
    • গ্লোব ?
  • শূন্য বা তার বেশি ক্যারেক্টারের সাথে মেলে
    • রেজেক্স .*
    • গ্লোব *
      • গ্লোব প্যাটার্ন .* মানে "এমন একটি ফাইলনেমের সাথে মেলা যার প্রথম ক্যারেক্টার একটি আক্ষরিক ডট, তারপর যেকোনো ক্যারেক্টারের শূন্য বা তার বেশি"।
  • এক বা তার বেশি ক্যারেক্টারের সাথে মেলে
    • রেজেক্স .+
    • গ্লোব ?*
  • রেজেক্সের ক্যারেক্টার সেট আর গ্লোবের ক্যারেক্টার সেট একইভাবে কাজ করে।
Advanced

Bash এক্সটেন্ডেড প্যাটার্ন দেয়, যেগুলো রেগুলার এক্সপ্রেশনের আরও কাছাকাছি।

কোটিং আর ফাইলনেম এক্সপেনশন

এমন সময় আসে যখন আপনি ফাইলনেম এক্সপেনশন চেপে দিতে চান। ইউজার ইনপুট হ্যান্ডল করা তেমনই একটি সময়। এই কোড স্নিপেটটি দেখুন

read -p "Enter something: " user_input
echo $user_input

ইউজার যদি একটি অ্যাস্টেরিস্ক (*) লিখেন, তাহলে আউটপুট কী আসবে? বর্তমান ডিরেক্টরির ফাইলের তালিকা। সম্ভবত আপনি এটাই চান না।

আরেকটি উদাহরণ হলো, প্যাটার্ন প্রত্যাশা করে এমন প্রোগ্রামে আর্গুমেন্ট হিসেবে প্যাটার্ন পাঠানো। টেক্সট ম্যানিপুলেশনের জন্য প্রায়ই tr প্রোগ্রামটি ব্যবহার করা হয়। একটি ভ্যারিয়েবলের মানকে ছোট হাতের অক্ষরে করতে আপনি এটি করতে পারেন

var="My Puppet"
echo "$var" | tr '[:upper:]' '[:lower:]'

আউটপুট দেয়

my puppet

একটি কমন ভুল হলো tr-এর আর্গুমেন্ট কোট করতে ভুলে যাওয়া: tr [:upper:] [:lower:]

Caution

এটা কেন সমস্যা? কোট ছাড়া গ্লোব প্যাটার্ন মিলে যাওয়া ফাইলের তালিকায় এক্সপ্যান্ড হয়ে যাবে। ধরুন, আপনার স্ক্রিপ্ট ব্যবহারকারী ব্যক্তির বর্তমান ডিরেক্টরিতে p, u আর w নামের ফাইল আছে:

touch p u w
var="My Puppet"
echo "$var" | tr [:upper:] [:lower:]

এতে একটি এরর হয়!

কোট ছাড়া [:upper:] প্যাটার্নটি :, u, p, e বা r যেকোনোটির সাথে মেলে। কোট ছাড়া [:lower:] প্যাটার্নটি :, l, o, w, e বা r যেকোনোটির সাথে মেলে। Bash প্রথম প্যাটার্নের জন্য p আর u ফাইল মেলায়, আর দ্বিতীয় প্যাটার্নের জন্য w ফাইল, তারপর tr এভাবে চালানো হয়

echo "$var" | tr p u w

আর এটা tr-এর জন্য ভুল সংখ্যক আর্গুমেন্ট।

ফাইলনেম এক্সপেনশন বন্ধ করা

ফাইলনেম এক্সপেনশন বন্ধ করতে আপনি set কমান্ডটি ব্যবহার করতে পারেন।

echo *      # prints a list of files
set -f      # disable filename expansion
echo *      # prints a literal asterisk
set +f      # enable filename expansion
ফাইলনেম এক্সপেনশন নিয়ন্ত্রণ করা

বিল্টইন shopt কমান্ডের একাধিক সেটিং আছে যেগুলো ফাইলনেম এক্সপেনশন কীভাবে কাজ করে তা নিয়ন্ত্রণ করে। এর মধ্যে কয়েকটি মজার সেটিং হলো:

  • shopt -s nocaseglob -- কেস-অসংবেদনশীল ম্যাচিং করে
  • shopt -s extglob -- এক্সটেন্ডেড প্যাটার্ন চালু করে
  • shopt -s nullglob -- কোনো ফাইল না মিললে প্যাটার্নটির জায়গায় কিছুই বসায় না। ডিফল্ট আচরণ হলো প্যাটার্নটিকে আক্ষরিক স্ট্রিং হিসেবে রেখে দেওয়া।
GitHub-এর মাধ্যমে সম্পাদনা করুন লিংকটি নতুন একটি উইন্ডো বা ট্যাবে খুলবে