কমান্ড লাইন আর্গুমেন্ট গ্রহণ করা (Accepting Command Line Arguments)
চলুন, বরাবরের মতো cargo new
ব্যবহার করে একটি নতুন প্রজেক্ট তৈরি করি। আমরা আমাদের প্রজেক্টের নাম দেব minigrep
, যাতে আপনার সিস্টেমে থাকা grep
টুল থেকে এটিকে আলাদা করা যায়।
$ cargo new minigrep
Created binary (application) `minigrep` project
$ cd minigrep
আমাদের প্রথম কাজ হলো minigrep
-কে তার দুটি কমান্ড লাইন আর্গুমেন্ট গ্রহণ করতে সক্ষম করা: ফাইলের পাথ এবং যে স্ট্রিংটি সার্চ করা হবে সেটি। অর্থাৎ, আমরা আমাদের প্রোগ্রামটি cargo run
দিয়ে চালাতে চাই, এরপর দুটি হাইফেন দিয়ে বোঝানো হবে যে পরের আর্গুমেন্টগুলো cargo
-র জন্য নয় বরং আমাদের প্রোগ্রামের জন্য, তারপর সার্চ করার জন্য একটি স্ট্রিং এবং সার্চ করার জন্য একটি ফাইলের পাথ থাকবে, যেমন:
$ cargo run -- searchstring example-filename.txt
এই মুহূর্তে, cargo new
দ্বারা তৈরি প্রোগ্রামটি আমাদের দেওয়া আর্গুমেন্টগুলো প্রসেস করতে পারে না। crates.io-তে কিছু লাইব্রেরি রয়েছে যা কমান্ড লাইন আর্গুমেন্ট গ্রহণ করে এমন প্রোগ্রাম লিখতে সাহায্য করতে পারে, কিন্তু যেহেতু আমরা এই ধারণাটি কেবল শিখছি, তাই আমরা এই ক্ষমতাটি নিজেরাই তৈরি করব।
আর্গুমেন্টের ভ্যালুগুলো পড়া (Reading the Argument Values)
minigrep
যাতে আমরা পাস করা কমান্ড লাইন আর্গুমেন্টের ভ্যালুগুলো পড়তে পারে, তার জন্য আমাদের Rust-এর standard library-তে থাকা std::env::args
ফাংশনটি ব্যবহার করতে হবে। এই ফাংশনটি minigrep
-এ পাস করা কমান্ড লাইন আর্গুমেন্টগুলোর একটি iterator রিটার্ন করে। আমরা অধ্যায় ১৩-তে iterator নিয়ে বিস্তারিত আলোচনা করব। আপাতত, iterator সম্পর্কে আপনার কেবল দুটি বিষয় জানলেই চলবে: iterator একটির পর একটি ভ্যালু তৈরি করে, এবং আমরা একটি iterator-এর উপর collect
মেথড কল করে এটিকে একটি কালেকশন, যেমন vector-এ পরিণত করতে পারি, যেখানে iterator দ্বারা তৈরি সমস্ত এলিমেন্ট থাকবে।
লিস্টিং ১২-১ এর কোডটি আপনার minigrep
প্রোগ্রামকে পাস করা যেকোনো কমান্ড লাইন আর্গুমেন্ট পড়তে এবং তারপর সেই ভ্যালুগুলোকে একটি vector-এ সংগ্রহ করতে সাহায্য করবে।
use std::env; fn main() { let args: Vec<String> = env::args().collect(); dbg!(args); }
প্রথমে আমরা use
স্টেটমেন্ট ব্যবহার করে std::env
মডিউলটিকে স্কোপে নিয়ে আসি যাতে আমরা এর args
ফাংশনটি ব্যবহার করতে পারি। লক্ষ্য করুন যে std::env::args
ফাংশনটি দুটি মডিউল লেভেলে নেস্টেড আছে। যেমনটি আমরা অধ্যায় ৭-এ আলোচনা করেছি, যেখানে কাঙ্ক্ষিত ফাংশনটি একাধিক মডিউলের মধ্যে নেস্টেড থাকে, সেখানে আমরা ফাংশনের পরিবর্তে প্যারেন্ট মডিউলটিকে স্কোপে নিয়ে এসেছি। এর মাধ্যমে, আমরা সহজেই std::env
থেকে অন্যান্য ফাংশন ব্যবহার করতে পারি। এটি use std::env::args
যোগ করে শুধুমাত্র args
দিয়ে ফাংশন কল করার চেয়ে কম দ্ব্যর্থক, কারণ args
-কে সহজেই বর্তমান মডিউলে ডিফাইন করা কোনো ফাংশন বলে ভুল হতে পারে।
args
ফাংশন এবং অবৈধ ইউনিকোডমনে রাখবেন যে
std::env::args
প্যানিক করবে যদি কোনো আর্গুমেন্টে অবৈধ ইউনিকোড (Unicode) থাকে। আপনার প্রোগ্রামে যদি অবৈধ ইউনিকোডযুক্ত আর্গুমেন্ট গ্রহণ করার প্রয়োজন হয়, তাহলে এর পরিবর্তেstd::env::args_os
ব্যবহার করুন। সেই ফাংশনটি একটি iterator রিটার্ন করে যাString
ভ্যালুর পরিবর্তেOsString
ভ্যালু তৈরি করে। আমরা এখানে সরলতার জন্যstd::env::args
ব্যবহার করেছি কারণOsString
ভ্যালুগুলো প্ল্যাটফর্ম ভেদে ভিন্ন হয় এবংString
ভ্যালুর চেয়ে কাজ করা বেশি জটিল।
main
-এর প্রথম লাইনে, আমরা env::args
কল করি এবং তাৎক্ষণিকভাবে collect
ব্যবহার করে iterator-টিকে একটি vector-এ পরিণত করি, যেখানে iterator দ্বারা তৈরি সমস্ত ভ্যালু থাকবে। আমরা collect
ফাংশনটি বিভিন্ন ধরণের কালেকশন তৈরি করতে ব্যবহার করতে পারি, তাই আমরা args
-এর টাইপ স্পষ্টভাবে Vec<String>
উল্লেখ করে দিই যে আমরা স্ট্রিং-এর একটি vector চাই। যদিও Rust-এ খুব কমই টাইপ উল্লেখ করার প্রয়োজন হয়, collect
এমন একটি ফাংশন যার জন্য প্রায়শই টাইপ উল্লেখ করতে হয়, কারণ Rust অনুমান করতে পারে না যে আপনি কোন ধরণের কালেকশন চান।
সবশেষে, আমরা ডিবাগ ম্যাক্রো ব্যবহার করে vector-টি প্রিন্ট করি। চলুন কোডটি প্রথমে কোনো আর্গুমেন্ট ছাড়া এবং তারপর দুটি আর্গুমেন্ট দিয়ে চালিয়ে দেখি:
$ cargo run
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.61s
Running `target/debug/minigrep`
[src/main.rs:5:5] args = [
"target/debug/minigrep",
]```
```console
$ cargo run -- needle haystack
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 1.57s
Running `target/debug/minigrep needle haystack`
[src/main.rs:5:5] args = [
"target/debug/minigrep",
"needle",
"haystack",
]
লক্ষ্য করুন যে ভেক্টরের প্রথম ভ্যালুটি হলো "target/debug/minigrep"
, যা আমাদের বাইনারির নাম। এটি C-তে আর্গুমেন্ট লিস্টের আচরণের সাথে মিলে যায়, যা প্রোগ্রামগুলোকে তাদের এক্সিকিউশনের সময় ব্যবহৃত নামটি ব্যবহার করতে দেয়। প্রোগ্রামের নামটি জানা প্রায়শই সুবিধাজনক, যদি আপনি মেসেজে এটি প্রিন্ট করতে চান বা প্রোগ্রামটি চালু করার জন্য কোন কমান্ড লাইন অ্যালিয়াস ব্যবহার করা হয়েছে তার উপর ভিত্তি করে প্রোগ্রামের আচরণ পরিবর্তন করতে চান। কিন্তু এই অধ্যায়ের জন্য, আমরা এটিকে উপেক্ষা করব এবং শুধুমাত্র আমাদের প্রয়োজনীয় দুটি আর্গুমেন্ট সেভ করব।
আর্গুমেন্টের ভ্যালুগুলো ভেরিয়েবলে সেভ করা (Saving the Argument Values in Variables)
প্রোগ্রামটি বর্তমানে কমান্ড লাইন আর্গুমেন্ট হিসেবে নির্দিষ্ট করা ভ্যালুগুলো অ্যাক্সেস করতে সক্ষম। এখন আমাদের দুটি আর্গুমেন্টের ভ্যালু ভেরিয়েবলে সেভ করতে হবে যাতে আমরা প্রোগ্রামের বাকি অংশে এই ভ্যালুগুলো ব্যবহার করতে পারি। এটি আমরা লিস্টিং ১২-২-এ করছি।
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
let query = &args[1];
let file_path = &args[2];
println!("Searching for {query}");
println!("In file {file_path}");
}
যেমনটি আমরা vector প্রিন্ট করার সময় দেখেছি, প্রোগ্রামের নামটি args[0]
-তে ভেক্টরের প্রথম স্থানটি নেয়, তাই আমরা ইনডেক্স ১ থেকে আর্গুমেন্ট শুরু করছি। minigrep
যে প্রথম আর্গুমেন্টটি নেয় তা হল আমরা যে স্ট্রিংটি খুঁজছি, তাই আমরা প্রথম আর্গুমেন্টের একটি রেফারেন্স query
ভেরিয়েবলে রাখি। দ্বিতীয় আর্গুমেন্টটি হবে ফাইল পাথ, তাই আমরা দ্বিতীয় আর্গুমেন্টের একটি রেফারেন্স file_path
ভেরিয়েবলে রাখি।
কোডটি আমাদের উদ্দেশ্য অনুযায়ী কাজ করছে কিনা তা প্রমাণ করার জন্য আমরা সাময়িকভাবে এই ভেরিয়েবলগুলোর ভ্যালু প্রিন্ট করি। চলুন এই প্রোগ্রামটি আবার test
এবং sample.txt
আর্গুমেন্ট দিয়ে চালাই:
$ cargo run -- test sample.txt
Compiling minigrep v0.1.0 (file:///projects/minigrep)
Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.0s
Running `target/debug/minigrep test sample.txt`
Searching for test
In file sample.txt
খুব ভালো, প্রোগ্রামটি কাজ করছে! আমাদের প্রয়োজনীয় আর্গুমেন্টগুলোর ভ্যালু সঠিক ভেরিয়েবলে সেভ হচ্ছে। পরে আমরা কিছু সম্ভাব্য ত্রুটিপূর্ণ পরিস্থিতি, যেমন ব্যবহারকারী কোনো আর্গুমেন্ট না দিলে, সেগুলো মোকাবেলা করার জন্য কিছু এরর হ্যান্ডলিং যোগ করব; আপাতত, আমরা সেই পরিস্থিতি উপেক্ষা করে ফাইল রিডিং ক্ষমতা যোগ করার দিকে মনোযোগ দেব।