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

রুবিতে শেল কমান্ডের STDOUT, STDERR এবং এক্সিট স্ট্যাটাস ক্যাপচার করুন

সংক্ষেপে: আপনি যদি রুবি থেকে শেল কমান্ড রান করে এর stdout, stderr এবং রিটার্ন স্ট্যাটাস ক্যাপচার করতে চান, তবে Open3.capture3 মেথড দেখুন। আপনি যদি stdout এবং stderr ডেটা স্ট্রিমিং ফ্যাশনে প্রসেস করতে চান, তবে Open3.popen3 দেখুন।

অনেক খারাপ পছন্দ

রুবি থেকে শেল কমান্ড এক্সিকিউট করার ফরমালি ৪৯২ টি পদ্ধতি আছে এবং প্রতিটা একে অপরের থেকে আলাদা কাজ করে। আমি দাবি করি আপনি নিচের পদ্ধতিগুলোর একটা ব্যবহার করেছেন। আমার পছন্দ সবসময় ব্যাক-টিক (``) ছিল।

exec("echo 'hello world'") # রুবি থেকে বের হয়, তারপর কমান্ড রান করে
system('echo', 'hello world') # স্ট্যাটাস কোড রিটার্ন করে
sh('echo', 'hello world') # স্ট্যাটাস কোড রিটার্ন করে
`echo "hello world"` # stdout রিটার্ন করে
%x[echo 'hello world'] # stdout রিটার্ন করে

কিন্তু এই পদ্ধতিগুলো খুব সীমিত। ধরুন, আপনাকে আপনার শেল কমান্ডের stdout এর পাশাপাশি stderr ও ক্যাপচার করতে হয়। আপনি আউট অফ লাক। অথবা ধরুন, আপনি stdout ডেটা স্ট্রিম হিসেবে প্রসেস করতে চান, কমান্ড শেষ হওয়ার জন্য অপেক্ষা না করে? আউট অফ লাক।

একটি আরো অপশন আছে। যা আপনাকে কমান্ড অ্যাসিনক্রোনাসলি রান দেয়, এবং stdout, stderr, এক্সিট কোড, এবং PID দেয়। চলুন দেখি!

Open3

অদ্ভুত নামে Open3 মডিউল রুবির স্ট্যান্ডার্ড লাইব্রেরির অংশ। এটি কি করে?

Open3 আপনাকে stdout, stderr, এক্সিট কোড এবং চাইল্ড প্রসেসের জন্য অপেক্ষা করার থ্রেডে অ্যাক্সেস দেয় অন্য প্রোগ্রাম রান করার সময়। আপনি প্রোগ্রামের বিভিন্ন অ্যাট্রিবিউট, রিডাইরেকশন, কারেন্ট ডিরেক্টরি ইত্যাদি নির্দিষ্ট করতে পারেন, ঠিক Process.spawn এর মতো। (সোর্স: Open3 Docs)

কभी ব্যবহার করেনি? কখনো শোনেনিন? আমি অনুমান করি এটা তখনই, কারণ এটি সবচেয়ে ফ্রেন্ডলি লাইব্রেরি হিসেবে মনে হয় না। নামটাই শোনা যায় সি-র চেয়ে রুবির মতো। এবং ডকুমেন্টেশন খুব হার্ড-কোর। কিন্তু একবার চেষ্টা করলে আপনি পাবেন এটা শোনার চেয়ে কম ভয়ঙ্কর।

capture3

কি হবে যদি stdout, stderr এবং স্ট্যাটাস কোড ক্যাপচার করার একটি সহজ পদ্ধতি থাকে? আছে। আপনার বাকি আর্টিকেল পড়ার সময় নেই, তবে শুধু জানুন capture3 মেথড ব্যবহার করে আপনি দিন শেষ করতে পারেন।

একটি উদাহরণ দেখি। ধরুন, আপনি কারেন্ট ডিরেক্টরির ফাইল লিস্ট পেতে চান। সেজন্য ls কমান্ড রান করতে পারেন।

ব্যাক-টিক সিনট্যাক্স ব্যবহার করলে এটা দেখাবে:

puts(`ls`)

capture3 দিয়ে এটা দেখাবে:

require 'open3'
stdout, stderr, status = Open3.capture3("ls")

এটি আপনার কমান্ড রান করবে এবং stdout এবং stderr স্ট্রিং হিসেবে দেবে। কোনো ঝামেলা নেই।

সিকিউরিটি

আপনি সাধারণত আপনার ইউজারদের আপনার ওয়েব সার্ভারে আর্বিট্রারি কমান্ড রান করার অনুমতি দিতে চান না। সেজন্য identify #{ params[:filename] } এর মতো কোড এতটাই ভয়ঙ্কর।

Open3 আপনাকে কমান্ড এবং ডেটা আলাদা করে এই সমস্যাগুলো এড়াতে দেয়। এটি system মেথডের মতো কাজ করে।

Open3.capture3("identify", params[:filename], other_unsafe_params)

popen3

হুডের নিচে, capture3 একটি আরও শক্তিশালী মেথড popen3 ব্যবহার করে। এই মেথডটি system() এর মতো পরিচিত মেথড থেকে একটু আলাদা কাজ করে।

এটা দেখতে এরকম:

require 'open3'
Open3.popen3("ls") do |stdout, stderr, status, thread|
  puts stdout.read
end

এটা একটু ফাইল খুলে পড়ার মতো। আমি নিশ্চিত আপনি এমন কোড দেখেছেন:

File.open("my/file/path", "r") do |f|
  puts f.read
end

পাইপস

Open3-এ stdout এবং stderr সব পাইপ, যেগুলো ফাইল বাফারের মতো আচরণ করে। এবং ফাইলের মতো, কাজ শেষ হলে সেগুলো ক্লোজ করতে হয়। সেজন্যই ব্লক সিনট্যাক্স। (নন-ব্লক সিনট্যাক্সও আছে, কিন্তু সেটাতে আপনাকে ম্যানুয়ালি stdout এবং stderr-এ close কল করতে হয়।)

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

এখানে stderr লাইন বাই লাইন ক্যাপচার করার পদ্ধতি:

require 'open3'
Open3.popen3("sleep 2; ls") do |stdout, stderr, status, thread|
  while line = stderr.gets
    puts line
  end
end

থ্রেডস

এখনও একটি আর্গুমেন্ট বাকি আছে। সেটা হলো thread।

thread আর্গুমেন্ট আপনাকে একটি রুবি থ্রেডের রেফারেন্স দেয় যা আপনার কমান্ড শেষ হওয়ার জন্য অপেক্ষা করছে। এখন, কমান্ডটি থ্রেডে রান করছে না। এটি সম্পূর্ণ আলাদা একটি প্রসেসে রান করছে। থ্রেডটি শুধু প্রসেসটি নজরদারি করে এবং শেষ হওয়ার জন্য অপেক্ষা করে।

আপনি সেই থ্রেড রেফারেন্স থেকে কিছু উপযোগী ডেটা পেতে পারেন।

  • thread.pid - আপনার শেল কমান্ডের প্রসেস আইডি ধারণ করে। আপনাকে এটা দরকার পড়বে যদি আপনি সেই প্রসেসের বিপরীতে অডিশনাল ওএস-লেভেল অপারেশন করতে চান।

  • thread.status - প্রসেসের এক্সিট স্ট্যাটাস ধারণ করে। সাকসেস বা ফেইলারের জন্য ১ বা ০।

সতর্কতা

Open3 ডক্স থেকে:

আপনাকে ডেডলক এড়াতে হব। যেহেতু পাইপগুলো ফিক্সড লেথ বাফার, ::popen3("prog") {|i, o, e, t| o.read } ডেডলক করবে যদি প্রোগ্রাম stderr-এ খুব বেশি আউটপুট জেনারেট করে। আপনাকে stdout এবং stderr একসাথে পড়তে হবে (থ্রেড বা IO.select ব্যবহার করে)। কিন্তু, আপনার যদি stderr আউটপুট দরকার না হয়, তবে আপনি ::popen2 ব্যবহার করতে পারেন। যদি মার্জড stdout এবং stderr আউটপুট সমস্যা না হয়, তবে আপনি ::popen2e ব্যবহার করতে পারেন। আপনার যদি সত্যিকারের stdout এবং stderr আলাদা স্ট্রিং হিসেবে দরকার হয়, তবে আপনি ::capture3 বিবেচনা করতে পারেন।
  1. OTG দিয়ে মোবাইল থেকে ফাইল ট্রান্সফার কীভাবে করবেন

  2. Fiddle দিয়ে যেকোনো C লাইব্রেরি রুবি থেকে এক্সেস করুন: রুবির লুকানো ক্ষমতা আনলক করুন

  3. রুবি WebSocket সার্ভার স্ক্র্যাচ থেকে বানান: ধাপে ধাপে গাইড

  4. Nginx কীভাবে HTTP হেডারগুলো আপনার Ruby অ্যাপে পাঠায়: একটি স্পষ্ট ও পেশাদার গাইড