সরাসরি শেল কমান্ড কার্যকর করার পরিবর্তে পাইথনের ওএস মডিউল পদ্ধতিগুলি কেন ব্যবহার করবেন?


157

আমি বুঝতে চেষ্টা করছি যে পাইথনের গ্রন্থাগার ফাংশনগুলি ওএস-সুনির্দিষ্ট কার্য সম্পাদনের জন্য যেমন ফাইল / ডিরেক্টরি তৈরি করা, ফাইলের বৈশিষ্ট্য পরিবর্তন করা ইত্যাদির পরিবর্তে কেবল সেই আদেশগুলি কার্যকর করার পরিবর্তে কী অনুপ্রেরণা হয়? os.system() বা subprocess.call()?

উদাহরণস্বরূপ, আমি কেন ব্যবহার করতে চাই os.chmod করছেন পরিবর্তে os.system("chmod...")?

আমি বুঝতে পেরেছি যে কেবল শেল কমান্ড সরাসরি চালানোর পরিবর্তে পাইথনের উপলব্ধ গ্রন্থাগার পদ্ধতি যথাসম্ভব ব্যবহার করা আরও "পাইথোনিক"। কিন্তু, কার্যকারিতা দৃষ্টিকোণ থেকে এটি করার পিছনে অন্য কোনও প্রেরণা আছে কি?

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


6
আপনি মূলত মাথায় পেরেক আঘাত। আপনি যে ওএস স্তরের কাজগুলিকে উল্লেখ করেছেন তা যথেষ্ট সাধারণ যে তারা ওএস.সিস্টেমের মাধ্যমে ডেকে পাঠানোর পরিবর্তে তাদের নিজস্ব ফাংশনটি চাঙ্গা করে।
deweyredman

7
বিটিডাব্লু, আপনি কি কার্যকর করার সময়টি চেষ্টা করেছিলেন - os.chmod বনাম ওএস.সিস্টেম ("chmod ...") । আমি একটি অনুমানকে ঝুঁকিপূর্ণ করব যে এটি আপনার প্রশ্নের অংশের উত্তর দেবে।
আগ্নেয়গিরি

61
কেন printযখন আপনি করতে পারেন os.system("echo Hello world!")?
ব্যবহারকারী 253751

25
একই কারণে আপনাকে os.pathপাথগুলি ম্যানুয়ালি হ্যান্ডেল করার পরিবর্তে পরিচালনা করতে হবে: এটি প্রতিটি ওএসে যেখানে এটি চালায় সেখানে কাজ করে।
বাকুরিউ

51
"শেল কমান্ড সরাসরি কার্যকর করা" আসলে কম সরাসরি is শেলটি সিস্টেমের জন্য একটি নিম্ন-স্তরের ইন্টারফেস os.chmodনয় chmodএবং শেলটি যে প্রোগ্রামটিকে কল করবে না। os.system('chmod ...')স্ট্রিংটি ব্যাখ্যা করতে শেল ব্যবহার করে সি chmodফাংশনে কল করার জন্য অন্য এক্সিকিউটেবলকে কল করার জন্য শেল ব্যবহার করে, যখন os.chmod(...)আরও সরাসরি সিটিতে যায় chmod
ব্যবহারকারী 2357112 মনিকা 4

উত্তর:


325
  1. এটি দ্রুততর , os.systemএবং subprocess.callএমন নতুন প্রক্রিয়া তৈরি করুন যা এই সাধারণ কোনও কিছুর জন্য অপ্রয়োজনীয়। আসলে, os.systemএবং subprocess.callসঙ্গে shellপ্রথম এক শেল হচ্ছে, এবং দ্বিতীয় আর মাত্র একটি কমান্ড হচ্ছে যে আপনি চালাচ্ছেন (যদি এটি একটি শেল বিল্ট-ইন মত নয়: যুক্তি সাধারণত কমপক্ষে দুটি নতুন প্রসেস তৈরি test)।

  2. কিছু কমান্ড পৃথক প্রক্রিয়াতে অকেজো । উদাহরণস্বরূপ, আপনি যদি চালনা করেন তবে os.spawn("cd dir/")এটি চাইল্ড প্রক্রিয়াটির বর্তমান ওয়ার্কিং ডিরেক্টরি পরিবর্তন করবে, কিন্তু পাইথন প্রক্রিয়াটির নয়। আপনি এটি জন্য ব্যবহার করা প্রয়োজন os.chdir

  3. শেল দ্বারা ব্যাখ্যা করা বিশেষ অক্ষর সম্পর্কে আপনাকে চিন্তা করার দরকার নেই । os.chmod(path, mode)ফাইলের নাম কী তা বিবেচনা করেই কাজ করবে, অন্যদিকে os.spawn("chmod 777 " + path)যদি ফাইলের নামটি এমন হয় তবে মারাত্মকভাবে ব্যর্থ হবে ; rm -rf ~। (মনে রাখবেন যে আপনি যদি তর্ক subprocess.callছাড়াই ব্যবহার করেন তবে আপনি এটিকে ঘিরে কাজ করতে পারেন shell))

  4. ড্যাশ দিয়ে শুরু হওয়া ফাইল নামগুলি নিয়ে আপনাকে চিন্তা করার দরকার নেই । os.chmod("--quiet", mode)নামের ফাইলটির অনুমতিগুলি পরিবর্তন করবে --quiet, তবে os.spawn("chmod 777 --quiet")ব্যর্থ হবে, যেমন --quietআর্গুমেন্ট হিসাবে ব্যাখ্যা করা হয়েছে। এটি এমনকি জন্য সত্য subprocess.call(["chmod", "777", "--quiet"])

  5. আপনার কাছে ক্রস-প্ল্যাটফর্ম এবং ক্রস শেল সংক্রান্ত উদ্বেগ কম রয়েছে, কারণ পাইথনের স্ট্যান্ডার্ড লাইব্রেরি আপনার পক্ষে এটি মোকাবেলা করার কথা। আপনার সিস্টেমে কি chmodআদেশ আছে? এটি ইনস্টল করা আছে? আপনি যে প্যারামিটারগুলি সমর্থন করবেন বলে সমর্থন করে তা কি সমর্থন করে? osমডিউল যখন এটি সম্ভব নয় যতটা সম্ভব ক্রস-প্ল্যাটফর্ম এবং দস্তাবেজগুলি হতে চেষ্টা করবে।

  6. আপনি যে কমান্ডটি চালাচ্ছেন তার যদি আপনার যত্ন করা আউটপুট থাকে তবে আপনাকে এটি বিশ্লেষণ করতে হবে যা এটি শোনার চেয়ে জটিল, কারণ আপনি কর্নার-কেসগুলি (স্পেস, ট্যাব এবং নতুন লাইনের সাথে ফাইলের নামগুলি) ভুলে যেতে পারেন, এমনকি আপনি যখন বহনযোগ্যতা সম্পর্কে চিন্তা করবেন না।


38
"ক্রস-প্ল্যাটফর্ম" পয়েন্টে যুক্ত করার জন্য, একটি ডিরেক্টরি তালিকাবদ্ধ করা হয় লিনাক্সের উপর "ls", উইন্ডোতে "dir"। ডিরেক্টরিতে বিষয়বস্তু পাওয়া খুব সাধারণ নিম্ন স্তরের কাজ।
আম্মোন কর্ট

1
@CortAmmon: "নিম্ন স্তরের" আপেক্ষিক, lsবা dirঠিক যেমন ডেভেলপারদের নির্দিষ্ট প্রকারের সুন্দর উচ্চ পর্যায়ে হয়, bashবা cmdবা kshবা যাই হোক না কেন শেল আপনি পছন্দ হয়।
সেবাস্তিয়ান মাচ

1
@ ফ্রেসনেল: আমি কখনই সেভাবে সেভাবে ভাবিনি। আমার কাছে, "আপনার ওএসের কার্নেল এপিআইতে সরাসরি কল" খুব নিম্ন স্তরের ছিল। আমি ধরে নিচ্ছি যে এ সম্পর্কে আলাদা দৃষ্টিভঙ্গি রয়েছে যা আমাকে ছাড়ছে কারণ আমি (স্বাভাবিকভাবেই) নিজের পক্ষপাতদুষ্টদের সাথে এটি পৌঁছাচ্ছি।
কর্ট অ্যামোন

5
@ কর্টআ্যামমন: ডান, এবং এর lsচেয়ে উচ্চতর স্তর, কারণ এটি আপনার ওএসের কার্নেল এপিআই-তে সরাসরি কল নয়। এটি একটি (ছোট) অ্যাপ্লিকেশন।
স্টিভ জেসোপ

1
@SteveJessop। আমি নিম্ন স্তরের "ডিরেক্টরিতে থাকা সামগ্রীর বিষয়বস্তুগুলি পাওয়া" বলেছি। আমি ভাবছি না lsবা dirতবে opendir()/readdir()(লিনাক্স এপিআই) বা FindFirstFile()/FindNextFile()(উইন্ডোজ এপিআই) বা File.listFiles(জাভা এপিআই) বা Directory.GetFiles()(সি #)। এই সমস্তগুলি ওএস-তে সরাসরি কল করার জন্য নিবিড়ভাবে আবদ্ধ। কারও কারও কারও পক্ষে নিবন্ধের মধ্যে নম্বর চাপানো এবং int 13hকার্নেল মোডে ট্রিগার করার মতো সহজ হতে পারে ।
কর্ন অ্যাম্মোন

133

এটি নিরাপদ। আপনাকে এখানে ধারণা দেওয়ার জন্য একটি উদাহরণ স্ক্রিপ্ট

import os
file = raw_input("Please enter a file: ")
os.system("chmod 777 " + file)

ব্যবহারকারীর কাছ থেকে যদি ইনপুটটি হয় test; rm -rf ~তবে এটি হোম ডিরেক্টরিটি মুছবে।

এই কারণে বিল্ট ইন ফাংশনটি ব্যবহার করা নিরাপদ।

সুতরাং আপনার সিস্টেমের পরিবর্তে সাব-প্রসেসটি কেন ব্যবহার করা উচিত।


26
বা এটি দেখার অন্য কোনও উপায়, ডান পাওয়া সহজ, পাইথন প্রোগ্রামগুলি লিখতে বা পাইথন প্রোগ্রামগুলি লিখুন যা শেল স্ক্রিপ্টগুলি লিখে? :-)
স্টিভ জেসপ

3
@ স্টিভ জেসাপ, আমার এক সহকর্মী অবাক হয়েছিলেন যে আমি একটি ছোট পাইথন স্ক্রিপ্ট তাকে 20 (!) গুণ বেশি দ্রুত ট্যান শেল স্ক্রিপ্ট লিখতে সাহায্য করেছি। আমি বুঝিয়েছি যে আউটপুট পুনর্নির্দেশটি দেখতে সেক্সি লাগবে - তবে এটি প্রতিটি পুনরাবৃত্তির উপর ফাইল খোলার এবং বন্ধ করার অন্তর্ভুক্ত। তবে কিছু কিছু কঠিন উপায়ে স্টাফ করতে পছন্দ করে - :)
আগ্নেয়গিরি

1
@ স্টিভ জেসাপ, এটি একটি কৌশল প্রশ্ন - রানটাইম অবধি আপনি জানতেন না! :)

60

সেখানে পাইথন এর আরো-নির্দিষ্ট পদ্ধতি ব্যবহার করা উচিত জন্য চার শক্তিশালী মামলা হয় osব্যবহার উপর মডিউল os.systemবা subprocessমডিউল যখন কমান্ড নির্বাহ:

  • অপ্রয়োজনীয় - অন্য প্রক্রিয়া তৈরি করা অতিরিক্ত কাজ এবং সময় এবং সংস্থান নষ্ট করে।
  • বহনযোগ্যতা - osমডিউলটির অনেকগুলি পদ্ধতি একাধিক প্ল্যাটফর্মে পাওয়া যায় যখন অনেকগুলি শেল কমান্ড ওএস-নির্দিষ্ট।
  • ফলাফল বুঝুন - আপনি আউটপুট থেকে ফলাফল বিশ্লেষণ এবং বুঝতে নির্বিচারে কমান্ড বাহিনী চালানো একটি প্রক্রিয়া ডিম ছাড়ার যদি এবং কেন কমান্ড কিছু ভুল করেছেন।
  • সুরক্ষা - কোনও প্রক্রিয়া তার প্রদত্ত যে কোনও কমান্ড সম্ভাব্যভাবে কার্যকর করতে পারে। এটি একটি দুর্বল নকশা এবং এটি osমডিউলটিতে নির্দিষ্ট পদ্ধতি ব্যবহার করে এড়ানো যায় ।

অপ্রয়োজনীয় ( রিডানড্যান্ট কোড দেখুন ):

আপনি প্রকৃতপক্ষে সিস্টেম কলগুলিতে যাওয়ার পথে একটি রিডানড্যান্ট "মিডল ম্যান" বাস্তবায়ন করছেন ( chmodউদাহরণ হিসাবে)। এই মাঝারি মানুষটি একটি নতুন প্রক্রিয়া বা উপ-শেল।

থেকে os.system:

সাব-শেল-এ কমান্ড (একটি স্ট্রিং) চালিত করুন ...

এবং subprocessনতুন প্রক্রিয়া স্প্যান করার জন্য একটি মডিউল মাত্র।

এই প্রক্রিয়াগুলি ব্যতীত আপনি যা প্রয়োজন তা করতে পারেন।

বহনযোগ্যতা ( উত্স কোড বহনযোগ্যতা দেখুন ):

osমডিউল উদ্দেশ্য জেনেরিক অপারেটিং সিস্টেম সেবা প্রদান করা হয় এবং এটি বর্ণনা শুরু সঙ্গে:

এই মডিউলটি অপারেটিং সিস্টেম নির্ভর কার্যকারিতা ব্যবহারের বহনযোগ্য উপায় সরবরাহ করে।

আপনি os.listdirউভয় উইন্ডো এবং ইউনিক্সে ব্যবহার করতে পারেন । এই কার্যকারিতাটির জন্য os.system/ ব্যবহার করার চেষ্টা আপনাকে subprocessদুটি কল বজায় রাখতে বাধ্য করবে (এর জন্য ls/ dir) এবং আপনি কোন অপারেটিং সিস্টেমের উপর আছেন তা যাচাই করে নিন। এটি তেমন পোর্টেবল নয় এবং পরে আরও হতাশার কারণ ঘটবে ( হ্যান্ডলিং আউটপুট দেখুন )।

কমান্ডের ফলাফলগুলি বোঝা:

মনে করুন আপনি ফাইলগুলিকে একটি ডিরেক্টরিতে তালিকাবদ্ধ করতে চান।

আপনি যদি os.system("ls")/subprocess.call(['ls']) কেবলমাত্র প্রক্রিয়াটির আউটপুট ফিরে পেতে পারেন যা মূলত ফাইলের নামের সাথে একটি বড় স্ট্রিং।

দুটি ফাইল থেকে তার নামের জায়গার কোনও ফাইলকে আপনি কীভাবে বলতে পারেন?

আপনার যদি ফাইলগুলি তালিকাভুক্ত করার অনুমতি নেই?

পাইথন অবজেক্টগুলিতে আপনার কীভাবে ডেটা ম্যাপ করা উচিত?

এগুলি কেবল আমার মাথার উপরের অংশে রয়েছে এবং যখন এই সমস্যার সমাধান রয়েছে - কেন আপনার সমস্যার সমাধান করা সমস্যা আবার কেন সমাধান করবেন?

এটি ইতিমধ্যে বিদ্যমান এবং আপনার জন্য নিখরচায় উপলব্ধ এমন কোনও বাস্তবায়ন পুনরাবৃত্তি না করে নিজেকে পুনরাবৃত্তি না করে নীতির (প্রায়শই "DRY" হিসাবে প্রকাশিত হয়) অনুসরণ করার একটি উদাহরণ ।

সুরক্ষা:

os.systemএবং subprocessশক্তিশালী হয়। আপনার যখন এই শক্তি প্রয়োজন তখন এটি ভাল তবে যখন আপনি এটি করবেন না তখন এটি বিপজ্জনক। আপনি যখন ব্যবহার করেন os.listdir, আপনি জানেন যে এটি অন্য কোনও কিছু করতে পারে না তবে ফাইলগুলি তালিকাবদ্ধ করতে বা ত্রুটি বাড়িয়ে তুলতে পারে। আপনি যখন ব্যবহার করেন os.systemবা subprocessএকই আচরণ অর্জন করতে পারেন তখন আপনি সম্ভবত এমন কিছু করে শেষ করতে পারেন যা আপনি করতে চাননি।

ইনজেকশন সুরক্ষা ( শেল ইঞ্জেকশন উদাহরণ দেখুন ) :

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

উদাহরণটি হ'ল ফর্মটির একটি আদেশ হবে:

# ... read some user input
os.system(user_input + " some continutation")

ইনপুটটি ব্যবহার করে যেকোন যথেচ্ছ কোড চালাতে সহজেই এটি ব্যবহার করা যেতে পারে: ইভেন্টটি NASTY COMMAND;#তৈরি করতে:

os.system("NASTY COMMAND; # some continuation")

এমন অনেক কমান্ড রয়েছে যা আপনার সিস্টেমকে ঝুঁকিতে ফেলতে পারে।


3
আমি বলব 2. মূল কারণ।
jaredad7

23

সাধারণ কারণে - আপনি যখন শেল ফাংশনটি কল করেন, এটি একটি সাব-শেল তৈরি করে যা আপনার কমান্ড উপস্থিত হওয়ার পরে ধ্বংস হয়ে যায়, সুতরাং আপনি যদি শেলের ডিরেক্টরি পরিবর্তন করেন - এটি পাইথনের আপনার পরিবেশকে প্রভাবিত করে না।

এছাড়াও, সাব-শেল তৈরি করা সময়সাপেক্ষ, সুতরাং ওএস কমান্ডগুলি সরাসরি ব্যবহার করা আপনার কর্মক্ষমতাকে প্রভাবিত করবে

সম্পাদনা

আমার কিছু সময় পরীক্ষা ছিল:

In [379]: %timeit os.chmod('Documents/recipes.txt', 0755)
10000 loops, best of 3: 215 us per loop

In [380]: %timeit os.system('chmod 0755 Documents/recipes.txt')
100 loops, best of 3: 2.47 ms per loop

In [382]: %timeit call(['chmod', '0755', 'Documents/recipes.txt'])
100 loops, best of 3: 2.93 ms per loop

অভ্যন্তরীণ ফাংশন 10 বারেরও বেশি দ্রুত চলে

EDIT2

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


কোনও সুযোগে আইপিথন দিয়ে কি তা করা হয়? ভাবেনি যে আপনি %সাধারণ দোভাষী ব্যবহার করে শুরু করে বিশেষ ফাংশন ব্যবহার করতে পারেন ।
আইপিগ্রামগ্রাম

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

1
অথবা আপনি একটি অজগর স্ক্রিপ্টও তৈরি করতে পারেন এবং তারপরে time <path to script> টার্মিনালটি টাইপ করতে পারেন এবং এটি আপনাকে আসল, ব্যবহারকারীর এবং প্রক্রিয়ার সময় বলে দেবে। এটি যদি আপনার আইপিথন না থাকে এবং আপনার ইউনিক্স কমান্ড লাইনে অ্যাক্সেস রয়েছে।
আইপোগ্রাম

1
@ এপিডি ডেভেলপার, আমি কঠোর পরিশ্রম করার কোনও কারণ দেখতে পাচ্ছি না - যখন আমার মেশিনে আইপিথন থাকবে
আগ্নেয়গিরি

সত্য! আমি বলেছিলাম যদি আপনার আইপিথন না থাকে। :)
আইপোগ্রাম

16

শেল কলটি ওএস নির্দিষ্ট হয় যেখানে বেশিরভাগ ক্ষেত্রে পাইথন ওএস মডিউল ফাংশন নেই। এবং এটি একটি সাবপ্রসেস তৈরি করা এড়াতে।


1
পাইথন মডিউল ফাংশনগুলি একটি নতুন সাবশেল শুরু করতে নতুন উপ-প্রসেসগুলিও স্পোন করে।
কোডারোক

7
@ কোডেরোক বাজে কথা, মডিউল ফাংশনগুলিকে প্রক্রিয়াজাতকরণ বলা হয়
ডুউরফ

3
@ কোডেরোক: ওএস মডিউলটি শেল কমান্ড ব্যবহার করে এমন অন্তর্নিহিত সিস্টেম কলগুলি ব্যবহার করে, এটি শেল কমান্ড ব্যবহার করে না। এর অর্থ হল শেল কমান্ডের চেয়ে ওএস সিস্টেম কলটি সাধারণত নিরাপদ এবং দ্রুত হয় (কোনও স্ট্রিং পার্সিং, বু কাঁটাচামচ না, এক্সিকিউটিভ হয় না, পরিবর্তে এটি কেবল একটি কার্নেল কল হয় না) shell মনে রাখবেন যে বেশিরভাগ ক্ষেত্রে শেল কল এবং সিস্টেম কলটির প্রায়শই একই বা একই নাম থাকে, তবে সেখানে আলাদাভাবে নথিভুক্ত করা হয়; শেল কলটি ম্যান সেকশন 1 (ডিফল্ট ম্যান সেকশন) এ রয়েছে এবং সমান নামযুক্ত সিস্টেম কলটি ম্যান সেকশন 2 এ রয়েছে (যেমন ম্যান 2 chmod)।
মিথ্যা রায়ান

1
@ দ্বারফ, লাইরিয়ান: আমার খারাপ! আমার একটা ভুল ধারণা ছিল, মনে হয়। ধন্যবাদ!
কোডারোক

11

এটি অনেক বেশি দক্ষ। "শেল" হ'ল আরেকটি ওএস বাইনারি যাতে প্রচুর সিস্টেম কল রয়েছে। কেন কেবল সেই একক সিস্টেমের কলের জন্য পুরো শেল প্রক্রিয়া তৈরির ওভারহেড ব্যয় করতে হবে?

পরিস্থিতি আরও খারাপ যখন আপনি os.systemএমন কিছু ব্যবহার করেন যা শেল্ট অন্তর্নির্মিত নয়। আপনি একটি শেল প্রক্রিয়া শুরু করেন যা ঘুরিয়ে একটি এক্সিকিউটেবল শুরু করে যা তারপরে (দুটি প্রক্রিয়া দূরে) সিস্টেম কল করে। অন্ততsubprocess শেল মধ্যস্থ প্রক্রিয়াটির প্রয়োজনীয়তা সরিয়ে ফেলতে হবে।

এটি পাইথনের সাথে নির্দিষ্ট নয়, এটি। systemdএকই কারণে লিনাক্স শুরুর সময়গুলিতে এমন উন্নতি ঘটেছে: এটি হাজার শাঁস তৈরির পরিবর্তে প্রয়োজনীয় সিস্টেমকে কল করে।

আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.