কম্পিউটার টিউটোরিয়াল

রুবিতে কমান্ড-লাইন অ্যাপ্লিকেশন মাস্টার করুন: একটি প্র্যাকটিক্যাল গাইড

ছোট ছোট ইউটিলিটি স্ক্রিপ্ট লিখতে ভালো লাগলেও, কখনও কখনও আপনাকে একটি আসল কমান্ড-লাইন অ্যাপ্লিকেশন লিখতে হয়। যেটি আর্গুমেন্ট নেয় এবং ইউনিক্স কনভেনশন অনুযায়ী ইনপুট, আউটপুট, এরর রিপোর্টিং ইত্যাদির সাথে ভালোভাবে কাজ করে।

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

ইনপুট: এনভায়রনমেন্ট ভেরিয়েবল

এনভায়রনমেন্ট ভেরিয়েবল সাধারণত সংক্ষিপ্ত কনফিগারেশন মান সংরক্ষণের জন্য ব্যবহৃত হয় যেগুলো কিছুক্ষণ ধরে রাখতে চান। API কী চমৎকার উদাহরণ।

এনভায়রনমেন্ট ভেরিয়েবল সেট করা

ব্যবহারকারীরা সাধারণত বাশের মতো শেলের মাধ্যমে এনভায়রনমেন্ট ভেরিয়েবল সেট করে। তারা নির্দিষ্ট সেশনের জন্য সেট করতে পারে:

$ AWS_ACCESS_KEY_ID=FOO 

বর্তমান সেশনের পлюস যেকোনো নতুন বাশ সেশনের জন্য সেট করতে পারে:

$ export AWS_ACCESS_KEY_ID=FOO 

অথবা একটি নির্দিষ্ট প্রোগ্রামের জন্য সেট করতে পারে:

$ env AWS_ACCESS_KEY_ID=FOO aws s3 mb s3://mybucket 

এনভায়রনমেন্ট ভেরিয়েবল পড়া

ব্যবহারকারী যেভাবেই এনভায়রনমেন্ট ভেরিয়েবল সেট করুক না কেন, আপনি রুবির ENV হ্যাশের মাধ্যমে তা পড়তে পারেন।

key_id = ENV["AWS_ACCESS_KEY_ID"] 

এনভায়রনমেন্ট ভেরিয়েবল কিভাবে কাজ করে?

প্রতিটি প্রসেসের সাথে একটি এনভায়রনমেন্ট ভেরিয়েবল টেবিল জড়িত থাকে। যখন এটি চাইল্ড প্রসেস তৈরি করে, তবে সেগুলো সেই টেবিলের একটি কপি পায় পেরেন্ট যেন করতে চায় তার পরিবর্তনসহ।

কি বুঝতে পারলেন?

আরো স্পষ্ট ব্যাখ্যার জন্য, আমার অন্য ব্লগ পোস্ট দেখুন: The Rubyist's Guide to Environment Variables।

কমান্ড-লাইন আর্গুমেন্ট

টার্মিনালে প্রোগ্রাম চালানোর সময় প্রোগ্রামের নামের পর যা কিছু লিখেন, সেটিই কমান্ড-লাইন আর্গুমেন্ট:

$ echo foo bar baz 

উপরোক্ত উদাহরণে "foo", "bar" এবং "baz" — সবই কমান্ড-লাইন আর্গুমেন্ট।

আরেকটি বাস্তব উদাহরণ হতে পারে:

$ honeybadger deploy -v --environment staging 

এখানেও কমান্ড-লাইন আর্গুমেন্টগুলো শুধু টেক্সট। যদি আমরা চাই --environment staging-এর কোনো অর্থ হোক, তবে আমাদের নিজেই সেটা বুঝতে হবে।

কমান্ড-লাইন আর্গুমেন্ট পাঠানো

আমরা ইতিমধ্যে দেখেছি আর্গুমেন্ট পাঠানো যেতে পারে এইভাবে:

$ echo foo bar baz 

কিন্তু যেহেতু রুবি একটি ইন্টারপ্রেটেড ভাষা, আপনি এরকম কিছু দেখতে পারেন:

$ ruby myprog.rb foo 

অপারেটিং সিস্টেমের দৃষ্টিতে, আপনি "ruby" নামে একটি প্রোগ্রাম চালাচ্ছেন এবং সে দুটি আর্গুমেন্ট পাচ্ছে।

সৌভাগ্যবশত, রুবি যথেষ্ট স্মার্ট যাতে বুঝতে পারে "foo" আর্গুমেন্টটি আপনার প্রোগ্রামের জন্য, রুবির জন্য নয়। তাই আপনার প্রোগ্রাম একটি আর্গুমেন্ট দেখবে: "foo"।

কমান্ড-লাইন আর্গুমেন্ট পড়া

কমান্ড-লাইন আর্গুমেন্টগুলো ARGV নামক একটি অ্যারেতে সংরক্ষিত থাকে। এটি একটি গ্লোভাল ভেরিয়েবল, তাই আপনার প্রোগ্রামের যেকোনো জায়গা থেকে অ্যাক্সেস করা যায়।

নিচের উদাহরণে, আমরা ARGV-এর সব কিছু প্রিন্ট করছি। আপনি চাইলে এটি কপি করে টার্মিনালে পেস্ট করে এক্সপেরিমেন্ট করতে পারেন।

$ ruby -e "puts ARGV.inspect" foo bar baz ["foo", "bar", "baz"] 

ruby -e বিষয়ে বিভ্রান্ত হওয়ার দরকার নেই। আমি এখানে শুধু এটি ব্যবহার করছি কারণ এটি প্রোগ্রাম এবং রান করার ফলাফল এক লাইনে দেখাতে দেয়।

কমান্ড-লাইন আর্গুমেন্ট পার্স করা

কমান্ড-লাইন আর্গুমেন্ট শুধু টেক্সট। আপনি যদি চান টেক্সটটির কোনো অর্থ হোক, তবে সেটা পার্স করতে হবে। সৌভাগ্যবশত, এই কাজে সাহায্য করার জন্য বেশ কিছু ভালো লাইব্রেরি রয়েছে।

বছরের পর বছর ধরে কমান্ড-লাইন আর্গুমেন্টের একটি কিছুটা স্ট্যান্ডার্ড সিনট্যাক্স উভয়েছে। সেটা দেখতে এভাবে:

$ program -a --option foo 

এই শৈলীতে আপনি বুলিয়ান ফ্ল্যাগ রাখতে পারেন (যেমন -h হেল্প দেখানোর জন্য)। এছাড়া ভ্যালুসহ অপশনও স্পেসিফাই করতে পারেন।

OptionParser পরিচিতি

OptionParser ক্লাস রুবির স্ট্যান্ডার্ড লাইব্রেরির অংশ। এটি উপরের শৈলীমতো অপশন পার্স করার সহজ পথ দেয়।

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

require 'optparse' # এটি পার্স করা অপশনগুলো ধারণ করবে options = {} OptionParser.new do |parser| # যখন -n বা --name দেখবে, সাথে আর্গুমেন্ট থাকলে, আর্গুমেন্টটি সংরক্ষণ করবে parser.on("-n", "--name NAME", "অভিবাদনের জন্য ব্যক্তির নাম।") do |v| options[:name] = v end end.parse! # এখন options হ্যাশ যেভাবে ইচ্ছে তেভাবে ব্যবহার করুন puts "Hello #{ options[:name] }" if options[:name] 

তখন রান করলে কাজ করে:

$ ruby hello.rb --name Starr Hello Starr $ ruby hello.rb -n Starr Hello Starr 

"হেল্প" স্ক্রিন যোগ করা

হেল্প ফিচার যোগ করা একসাথে সহজ। শুধু কিছু টেক্সট দিয়ে -h কমান্ড যোগ করলেই হবে:

OptionParser.new do |parser| parser.banner = "Usage: hello.rb [options]" parser.on("-h", "--help", "এই হেল্প মেসেজ দেখাও") do || puts parser end ... end.parse! 

এখন হেল্প চাইলে:

$ ruby hello.rb -h Usage: hello.rb [options] -h, --help Show this help message -n, --name NAME The name of the person to greet. 

আর্গুমেন্ট টাইপকাস্ট করা

সকল কমান্ড-লাইন আর্গুমেন্ট স্ট্রিং, কিন্তু কখনও কখনও আপনি চান ব্যবহারকারী আপনাকে নম্বর বা তারিখ দেবে। হাতে রূপান্তর করা কষ্টসাধ্য, তাই OptionParser আপনার জন্য করে দেয়।

নিচের উদাহরণে, আমি একটি "count" অপশন যোগ করছি যাতে ব্যবহারকারী বলে দে প্রোগ্রামটি কতবার হ্যালো বলবে। আমি OptionParser-কে Integer-এ কাস্ট করতে বলছি, যা সেটা করে দেয়।

OptionParser.new do |parser| ... # লক্ষ্য করুন `Integer` আর্গুমেন্টটি। এটি পার্সারকে বলে মানটি ইন্টিজারে কাস্ট করবে। # আমি `Float`, `Date`, বা অন্যান্য টাইপও ব্যবহার করতে পারতাম। parser.on("-c", "--count COUNT", Integer, "মেসেজ COUNT বার রিপিট করুন") do |v| options[:count] = v end end.parse! if options[:name] options.fetch(:count, 1).times do puts "Hello #{ options[:name] }" end end 

এখন প্রোগ্রাম রান করলে আমি একাধিকবার অভিবাদন পাচ্ছি:

$ ruby hello.rb -n Starr -c 5 Hello Starr Hello Starr Hello Starr Hello Starr Hello Starr 

নামকরণ কনভেনশন

প্রোগ্রামিং-এ সবচেয়ে কঠিন দিকের একটি হলো কীকে কী নাম দেব। কমান্ড-লাইন আর্গুমেন্টও এর ব্যতিক্রম নয়। আপনাকে সাহায্য করার জন্য, আমি কিছু সাধারণ আর্গুমেন্ট এবং তাদের অর্থের একটি ছোট টেবিল তৈরি করেছি:

ফ্ল্যাগসাধারণ অর্থ
-aAll, Append
-dডিবাগ মোড, অথবা ডিরেক্টরি স্পেসিফাই
-eকিছু এক্সিকিউট করুন, অথবা এডিট করুন
-fফাইল স্পেসিফাই করুন, অথবা অপারেশন ফোর্স করুন।
-hহেল্প
-mএকটি মেসেজ স্পেসিফাই করুন
-oআউটপুট ফাইল বা ডিভাইস স্পেসিফাই করুন
-qকয়েট মোড
-vভার্বোজ মোড। বর্তমান ভার্সন প্রিন্ট করুন
-yযেকোনো প্রম্পটে "yes" বলুন

OptionParser-এর বিকল্প

OptionParser ভালো, কিন্তু এর কিছু লিমিটেশন রয়েছে। সবচেয়ে স্পষ্টতঃ, এটি gần年の популяр কমান্ড-ভিত্তিক সিনট্যাক্স সাপোর্ট করে না:

$ myprog command subcommand -V 

সৌভাগ্যবশত, অপশন পার্সিং লাইব্রেরির প্রচুর রয়েছে। আপনি অবশ্যই একটি পছন্দ করবেন। এখানে তিনটি আকর্ষণীয় লাইব্রেরি দেওয়া হলো:

  • GLI - "Git-like Interface" কমান্ড-লাইন পার্সার যা আপনাকে সহজে গিটের মতো অ্যাপ্লিকেশন বানাতে দেয় — একটি এক্সিকিউটেবল একাধিক কমান্ডসহ।
  • CRI - নেস্টেড কমান্ড সাপোর্টসহ কমান্ড-লাইন টুল বানানোর সহজ লাইব্রেরি।
  • Methadone - Methadone নিজস্ব অপশন পার্সিং DSL দেয়, কিন্তু সেটা তার চেয়েও অনেক বেশি। এটি আপনার জন্য ডিরেক্টরি স্ট্রাকচার, টেস্ট স্যুট এবং লগিং সেট আপ করে দেয়। এটি আপনার CLI-এর জন্য রেইলসের মতো।

STDIN দিয়ে বড় পরিমাণ ডেটা ইনপুট করা

কমান্ড-লাইন আর্গুমেন্ট বড় পরিমাণ ডেটা ইনপুটের জন্য উপযুক্ত নয়। সেটার জন্য আপনাকে IO স্ট্রিম ব্যবহার করতে হবে। এবং STDIN সবচেয়ে সুবিধাজনক IO স্ট্রিমের একটি।

অधিকাংশ প্রোগ্রামকে অপারেটিং সিস্টেম অটোমেটিক্যালি একটি STDIN দিয়ে দেয়। এটি একটি রিড-অনলি ফাইলের মতো যা OS আপনার অ্যাপে ডেটা পাঠাতে ব্যবহার করে। এটি ব্যবহারকারীর কীবোর্ড ইনপুট लेनेতে ব্যবহার করা যায়। কিন্তু更重要的是, এটি পাইপের মাধ্যমে অন্য প্রোগ্রাম থেকে ডেটা গ্রহণ করতে ব্যবহার করা যায়।

আপনি STDIN-কে যেন রিড-অনলি ফাইল হয় তেন ব্যবহার করতে পারেন। এখানে, আমি কিছু টেক্সট আমার প্রোগ্রামের STDIN-এ পাইপ করছি। তারপর read মেথড দিয়ে তা ফেচ করছি।

$ echo "hello world" | ruby -e "puts STDIN.read.upcase" HELLO WORLD 

রুবিতে, আপনি IO অবজেক্টে Enumerable ফিচার ব্যবহার করতে পারেন। এবং STDIN তার ব্যতিক্রম নয়। এর মানে আপনি সব soort ট্রিক করতে পারেন:

# প্রথম ২০ লাইন পান STDIN.first(20) # ইন্টিজারে কনভার্ট করে অড্ড সংখ্যা রিজেক্ট করুন STDIN.map(&:to_i).reject(&:odd) ...etc 

STDOUT-এ রেজাল্ট আউটপুট করা

STDOUT একটি রাইট-অনলি IO স্ট্রিম যা অপারেটিং সিস্টেম আপনার প্রোগ্রামে অ্যাসাইন করে।

STDOUT-এ লিখলে আপনি ব্যবহারকারীর টার্মিনালে টেক্সট পাঠাতে পারেন। ব্যবহারকারী আউটপুট ফাইলে রিডাইরেক্ট করতে পারে, অথবা অন্য প্রোগ্রামে পাইপ করতে পারে।

আপনি STDOUT-কে যেন অন্য যেকোনো রাইট-অনলি ফাইল হয় তেন ব্যবহার করবেন:

STDOUT.write("Hi!\n") # hi 

এবং অবশ্যই, puts এবং print দুটোই STDOUT-এ আউটপুট করে।

STDERR-এ স্ট্যাটাস ইনফরমেশন পাঠানো

STDERR আরও একটি রাইট-অনলি IO স্ট্রিম। এটি জেনারেল আউটপুটের জন্য নয়। এটি নির্দিষ্টভাবে স্ট্যাটাস মেসেজের জন্য — যাতে সেগুলো আসল আউটপুটের বাধা না দেয়।

STDERR সাধারণত ব্যবহারকারীর টার্মিনালে দেখাবে, এমনকি যদি তারা STDOUT ফাইলে বা অন্য প্রোগ্রামে রিডাইরেক্ট করে।

নিচের উদাহরণে আমরা curl ব্যবহার করে একটি ওয়েবপেজ ফেচ করছি। এটি ওয়েবপেজের কন্টেন্ট STDOUT-এ আউটপুট করে, এবং প্রগ্রেস ইনফরমেশন STDERR-এ দেখায়:

$ curl "https://www.google.com/" > /tmp/g.html % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 151k 0 151k 0 0 277k 0 --:--:-- --:--:-- --:--:-- 277k 

নিজের প্রোগ্রাম থেকে STDERR-এ লেখা সহজ। এটাকে IO অবজেক্ট মানেই ট्रीট করুন:

STDERR.write("blah\n") STDERR.puts("blah") 

সুন্দর করা (Hitting it with the pretty stick)

আমরা যা উদাহরণ দেখেছি সেগুলো কোনো ডিজাইন অ্যাওয়ার্ড জিতবে না। কিন্তু কমান্ড-লাইন অ্যাপ্লিকেশন বদসूरत বা নন-ইন্টারেক্টিভ হবার কোনো কারণ নেই।

আপনি যদি অ্যাপটি সাজাতে চান, তবে অনেক চমৎকার জেম রয়েছে যা ভারী লিফ্টিং করবে। এখানে তিনটি আমার পছন্দের:

  • highline - Highline ব্যবহারকারীর ইনপুট গेदারিং, ভ্যালিডেটিং এবং টাইপকাস্টিং-এর অনেক কাজ কমায় দেয়।
  • command_line_reporter - ASCII প্রগ্রেস রিপোর্ট জেনারেট করা সহজ করে।
  • paint - আপনার নীরস টেক্সটে ANSI কালার কোড যোগ করে কালারফুল করা সহজ করে।

আরও বিস্তারিত তালিকা দেখুন

একজিট স্ট্যাটাস

যদি আপনার প্রোগ্রাম এরর সহ একজিট করে, তবে আপনাকে একজিট কোড দিয়ে OS-কে জানাতে হবে। তবেই বাশ কোডের ধরণ এই কাজ করে:

$ prog1 && prog2 

যদি আপনি বাশের && অপারেটর জানেন না, তবে এটি সহজ অর্থে "prog2 তभी রান করবে যদি prog1 সফলভাবে একজিট করে।"

রুবি ডিফল্টভাবে "সাকসেস" কোড দিয়ে একজিট করে, এবং এক্সেপশন হলে "ফেইলার" কোড। বাকি সব আপনাকে ইমপ্লিমেন্ট করতে হবে। সৌভাগ্যবশত, এটি সহজ:

# `exit`-এ যেকোনো নন-জেরো আর্গুমেন্ট ফেইলার বলে গণ্য হয় exit(1) 

আরও জানতে, আমার অন্য পোস্ট দেখুন: How to exit a Ruby program।

প্রসেস নাম সেট করা

যদি আপনার কমান্ড-লাইন প্রোগ্রাম কিছুক্ষণ চলে, তবে সিস্টেম প্রসেস লিস্ট করার সময় লোকেরা কী করছে সেটা বুঝতে পারা গুরুত্বপূর্ণ। সাধারণত, একটি রুবি প্রোগ্রামের প্রসেস নাম হলো আপনি যা টাইপ করে প্রোগ্রাম রান করেছেন। যেন আপনি টাইপ করলেন ruby myapp -v, সেটিই প্রসেসের নাম।

যদি আপনার প্রোগ্রামে অনেক আর্গুমেন্ট থাকে, তবে এটি অনরিডেবল হয়ে যেতে পারে। তাই আপনি একটি বান্ধব প্রসেস নাম সেট করতে চাইতে পারেন। এভাবে করতে পারেন:

Process.setproctitle("My Awesome Command Line App") 

আপনি যদি ফ্যান্সি হতে চান, তবে প্রসেস টাইটেল ব্যবহার করে ব্যবহারকারীকে বর্তমানে প্রসেস কী করছে সেটা বোঝাতে পারেন।

Process.setproctitle("Mail Sender: initializing") init(...) Process.setproctitle("Mail Sender: connecting") connect(...) Process.setproctitle("Mail Sender: sending") send(...) 

আরও জানতে, আমার পোস্ট দেখুন: How to change the process name of your Ruby script as shown by top and ps

  1. রুবিতে ল্যাম্বডা মাস্টার করুন: বেসিক থেকে অ্যাডভান্সড টেকনিক

  2. পাওয়ারফুল রুবি CLI টুল বিল্ড করুন: একটি স্টেপ-বাই-স্টেপ গাইড

  3. রুবি ২.৬-এ ৯টি গুরুত্বপূর্ণ উন্নতির অন্বেষণ

  4. টপ ৮ আইপ্যাড রাইটিং অ্যাপ: প্রোডাক্টিভিটি ও ক্রিয়েটিভিটি বাড়ান