পাইথন স্ক্রিপ্টের মধ্যে থেকে ইউএসি উন্নয়নের জন্য অনুরোধ করবেন?


94

আমি চাই আমার পাইথন স্ক্রিপ্টটি ভিস্তার উপর ফাইলগুলি অনুলিপি করতে। আমি যখন এটি একটি সাধারণ cmd.exeউইন্ডো থেকে চালিত করি তখন কোনও ত্রুটি উত্পন্ন হয় না, তবুও ফাইলগুলি অনুলিপি করা হয় না। আমি যদি cmd.exe"অ্যাডমিনিস্ট্রেটর হিসাবে" চালনা করি এবং তারপরে আমার স্ক্রিপ্টটি চালিত করি, এটি দুর্দান্ত কাজ করে।

ইউজার অ্যাকাউন্ট কন্ট্রোল (ইউএসি) সাধারণত অনেকগুলি ফাইল সিস্টেমের ক্রিয়া প্রতিরোধ করে বলে এটি বোঝা যায়।

পাইথন স্ক্রিপ্টের মধ্যে থেকে কি কোনও উপায় আছে, আমি একটি ইউএসি উন্নয়নের অনুরোধ জানাতে পারি ("এই জাতীয় অ্যাপ্লিকেশনটিতে অ্যাডমিন অ্যাক্সেসের দরকার আছে এমন কথোপকথনগুলি কি ঠিক আছে?")

যদি এটি সম্ভব না হয় তবে আমার স্ক্রিপ্টটি কি কমপক্ষে সনাক্ত করতে পারে যে এটি উচ্চতর নয় যাতে এটি নিখুঁতভাবে ব্যর্থ হতে পারে?


4
stackoverflow.com/a/1445547/1628132 এই উত্তরের অনুসরণ করে আপনি .py স্ক্রিপ্ট থেকে পাই 2xe ব্যবহার করে এবং 'uac_info' নামক একটি পতাকা ব্যবহার করে এটি খুব সুন্দর সমাধান
ফক্সকোরগ

উত্তর:


100

2017 হিসাবে, এটি অর্জনের একটি সহজ পদ্ধতি নিম্নলিখিত:

import ctypes, sys

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        return False

if is_admin():
    # Code of your program here
else:
    # Re-run the program with admin rights
    ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, " ".join(sys.argv), None, 1)

আপনি যদি পাইথন ২.x ব্যবহার করছেন তবে আপনার জন্য সর্বশেষ লাইনটি প্রতিস্থাপন করা উচিত:

ctypes.windll.shell32.ShellExecuteW(None, u"runas", unicode(sys.executable), unicode(" ".join(sys.argv)), None, 1)

এছাড়াও মনে রাখবেন যদি আপনি একটি এক্সিকিউটেবল ফাইলের মধ্যে পাইথন স্ক্রিপ্ট রূপান্তরিত (যেমন সরঞ্জাম ব্যবহার py2exe, cx_freeze, pyinstaller) তারপর আপনি ব্যবহার করা উচিত sys.argv[1:]পরিবর্তে sys.argvচতুর্থ প্যারামিটার হবে।

এখানে কিছু সুবিধা রয়েছে:

  • কোন বাহ্যিক গ্রন্থাগার প্রয়োজন। এটি শুধুমাত্র ব্যবহার করে ctypesএবং sysস্ট্যান্ডার্ড লাইব্রেরি থেকে।
  • পাইথন 2 এবং পাইথন 3 উভয় নিয়েই কাজ করে।
  • ফাইল সংস্থানগুলি পরিবর্তন করার বা ম্যানিফেস্ট ফাইল তৈরি করার দরকার নেই।
  • যদি আপনি নীচে কোড যোগ না করেন / অন্য বিবৃতি, কোডটি কখনও দু'বার কার্যকর করা হবে না।
  • আপনি শেষ লাইনে এপিআই কলটির রিটার্ন মান পেতে পারেন এবং এটি ব্যর্থ হলে একটি পদক্ষেপ নিতে পারেন (কোড <= 32)। এখানে সম্ভাব্য ফেরতের মানগুলি পরীক্ষা করুন
  • আপনি ছয় প্যারামিটার পরিবর্তন করে তৈরি প্রসেসের ডিসপ্লে পদ্ধতিটি পরিবর্তন করতে পারেন।

অন্তর্নিহিত ShellExecute কলের জন্য ডকুমেন্টেশন এখানে


9
এই চলমানটি পেতে আমাকে শেলেক্সেক্সিউটডাব্লু (যেমন ইউরুনাস 'এবং ইউনিকোডের (সিএস। এক্সেকিউটেবল)) হিসাবে প্যারামিটার হিসাবে ইউনিকোড উদাহরণ ব্যবহার করতে হয়েছিল।
জানোশ

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

4
@ মার্টিন যদি আমি এই কোডটি উইন্ডোজ কমান্ড লাইন থেকে এইভাবে চালাচ্ছি: "পাইথন আপনার কোড কোড.পি" এটি কেবল পাইথন.এক্সই খুলবে। এটা ঠিক করার কোন উপায় আছে?
ব্যবহারকারী 2978216

4
@ user2978216 আমার একই সমস্যা ছিল। লাইনে ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, "", None, 1) sys.executableকেবল পাইথন ইন্টারপ্রেটারের C:\Python27\Python.exeসমাধান করা হয় (উদাহরণস্বরূপ ) সমাধানটি চলমান স্ক্রিপ্টটিকে আর্গুমেন্ট হিসাবে যুক্ত করা হয় (প্রতিস্থাপন "")। ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, __file__, None, 1)এছাড়াও লক্ষ করুন, অজগর ২.x এ কাজ করার জন্য, সমস্ত স্ট্রিং আর্গুমেন্টের ইউনিকোড হওয়া প্রয়োজন (যেমন u"runas", unicode(sys.executable)এবং unicode(__file__))
জাভিয়ের উবিলোস

4
উভয় @HrvojeT, ShellExecuteWএবং ShellExecuteAকল হয় ShellExecuteউইন্ডোজ API এ ফাংশন। পূর্বের স্ট্রিংগুলি ইউনিকোড ফর্ম্যাটে থাকতে বাধ্যতামূলক এবং পরবর্তীটি এএনএসআই ফর্ম্যাটের সাথে ব্যবহার করা হয়
মার্টন ডি লা ফুয়েন্ট

71

ডুয়ারাগ্লিয়ার উত্তরটি কাজ করতে আমার একটু সময় লেগেছে, তাই অন্যের সময় বাঁচানোর স্বার্থে, এই ধারণাটি বাস্তবায়নের জন্য আমি এখানে যা করেছি:

import os
import sys
import win32com.shell.shell as shell
ASADMIN = 'asadmin'

if sys.argv[-1] != ASADMIN:
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + [ASADMIN])
    shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params)
    sys.exit(0)

4
এটি কেবল উন্নত হবে এবং তারপরে প্রস্থান হবে বলে মনে হচ্ছে ... আমি যদি কিছু মুদ্রণ বিবৃতি পেশ করি তবে তারা দ্বিতীয়বার মৃত্যুদণ্ড কার্যকর করতে পারবে না
জোড়ান বিসলে

6
@ জোড়ান বিসলে, আপনি কোনও আউটপুট দেখতে পাবেন না। ShellExecuteEx এর STDOUT আরম্ভকারী শেলের কাছে পোস্ট করে না। সেই ক্ষেত্রে, ডিবাগিং হবে ... চ্যালেঞ্জিং। তবে অধিকার-উত্তোলনের কৌশলটি অবশ্যই কাজ করে।
টিম কেটিং

4
@ টিমকিটিং, অ্যাক্টিভেটের একটি রেসিপি রয়েছে যা ডিবাগিংকে আরও সহজ করে তুলতে হবে: স্ট্যান্ডার্ড পাইথন লগিংয়ের সাথে ডিবাগভিউ ইউটিলিটিটি ব্যবহার করুন
স্যামউইজ

4
একই কনসোলে আউটপুট পাওয়া অসম্ভব বলে মনে হচ্ছে, তবে শেলএকসেকিউটেক্সে nShow = 5 টি যুক্তির সাহায্যে এলিভেটেড স্ক্রিপ্ট থেকে আউটপুট সহ একটি নতুন কমান্ড উইন্ডো খোলা হবে।
এমিল স্টায়ার্ক

4
উদ্ধৃতি দেওয়ার জন্য, আপনি subprocess.list2cmdlineএটি সঠিকভাবে করতে ব্যবহার করতে পারেন ।
কোডারফরফ্লাই

29

মনে হচ্ছে কোনও নির্দিষ্ট কাজ সম্পাদনের জন্য কিছু সময়ের জন্য অ্যাপ্লিকেশন সুবিধাটি উন্নত করার কোনও উপায় নেই। উইন্ডোজটিকে প্রোগ্রামটির শুরুতে এটি জানতে হবে যে অ্যাপ্লিকেশনটির কিছু বিশেষ সুযোগ রয়েছে কিনা এবং অ্যাপ্লিকেশনটি কোনও সুযোগসুবিধিতে প্রয়োজনীয় কোনও কার্য সম্পাদন করে যখন ব্যবহারকারীকে তা নিশ্চিত করতে জিজ্ঞাসা করবে । এই কাজটি করার দুটি পদ্ধতি আছে:

  1. ম্যানিফেস্ট ফাইল লিখুন যা উইন্ডোজকে বলে যে অ্যাপ্লিকেশনটিতে কিছু সুযোগ-সুবিধার দরকার রয়েছে
  2. অন্য কোনও প্রোগ্রামের ভিতরে থেকে উন্নত সুবিধাসমূহ নিয়ে অ্যাপ্লিকেশনটি চালান

এই দুটি নিবন্ধটি আরও কীভাবে এটি কাজ করে তা বিশদে ব্যাখ্যা করে।

আমি কী করব, যদি আপনি ক্রিয়েটএলভেটেডপ্রসেস এপিআইয়ের জন্য কোনও বাজে সিটিপস র‌্যাপারটি লিখতে না চান, কোড প্রকল্প অনুচ্ছেদে বর্ণিত শেলএক্সেকিউটেক্স ট্রিকটি ব্যবহার করা হয় (পিনউইন 32 শেলএক্সেকুটের জন্য একটি মোড়ক নিয়ে আসে)। কীভাবে? এটার মতো কিছু:

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

আপনি আপনার প্রোগ্রামটিকে "স্ক্রিপ্ট" হিসাবে বর্ণনা করার সাথে সাথে আমি মনে করি এটি আপনার প্রয়োজনের জন্য যথেষ্ট।

চিয়ার্স


এই লিঙ্কগুলির জন্য ধন্যবাদ, তারা আমার জন্য ইউএসি স্টাফ সম্পর্কে অনেক কিছু খুঁজে পেতে খুব দরকারী ছিল were
কোলেন

4
আপনি যা লক্ষ করতে চান তা হ'ল আপনি পাইউইন 32 ছাড়াই শেলএক্সেকুট করতে পারেন (ওটি ইনস্টল করতে আমার সমস্যা হয়েছিল) os.startfile ($ এক্সিকিউটেবল, "রানাস") ব্যবহার করে।
মাইক ম্যাককয়েড

@ মাইক - তবে runasএকটি নতুন প্রম্পট এনেছে। এবং স্টার্টফিল কমান্ড লাইন আর্গুমেন্টগুলি গ্রহণ করে না$EXECUTABLE.
শ্রীধর রত্নকুমার

আমি এই কৌশলটির সম্পূর্ণ বাস্তবায়নের সাথে আরও একটি উত্তর যুক্ত করেছি যা কোনও অজগর স্ক্রিপ্টের শুরুতে যুক্ত হতে সক্ষম হওয়া উচিত।
জোরেঙ্কো

দ্বিতীয় লিঙ্কটির নিবন্ধটি ছিল "ন্যূনতম অধিকার: আপনার অ্যাপ্লিকেশনগুলিকে উইন্ডোজ ভিস্তার ব্যবহারকারী অ্যাকাউন্ট নিয়ন্ত্রণের সাথে সুন্দরভাবে খেলতে শেখান" "এমএসডিএন ম্যাগাজিন জানুয়ারী ২০০ 2007" তে, তবে এই সমস্যাটি এখন কেবল .chmফাইল হিসাবে উপলভ্য ।
পিটার

6

আমি যেমন ছিলাম তেমনই অন্যদের এখানে গুগল অনুসন্ধান দ্বারা নির্দেশিত করার ক্ষেত্রে এই উত্তরটি যুক্ত করুন। আমি elevateআমার পাইথন স্ক্রিপ্টে মডিউলটি ব্যবহার করেছি এবং উইন্ডোজ 10 এ অ্যাডমিনিস্ট্রেটর প্রাইভেলিজের সাথে সম্পাদিত স্ক্রিপ্টটি।

https://pypi.org/project/elevate/


আরে, আমি elevateমডিউলটি ব্যবহার করার চেষ্টা করেছি এবং আমি "ফাইলটি সিস্টেম দ্বারা অ্যাক্সেস করা যায় না" ত্রুটি পেয়েছি, কোনও ধারণা কেন এমন হবে?
paxos1977

@ paxos1977 আপনি কি কোনও কোড স্নিপেট পোস্ট করতে পারেন যা সেই ত্রুটিটি দেখায়? ধন্যবাদ!
ইরিভিং ময়

5

নিম্নলিখিত উদাহরণটি মার্টিন ডি লা ফিউন্টে সাভেদেরার দুর্দান্ত কাজ এবং গৃহীত উত্তরের উপর ভিত্তি করে । বিশেষত, দুটি গণনা চালু করা হয়। প্রথমটি কীভাবে একটি এলিভেটেড প্রোগ্রামটি খোলার সহজ স্পেসিফিকেশন করার অনুমতি দেয় এবং দ্বিতীয়টি যখন ত্রুটিগুলি সহজে চিহ্নিত করা দরকার তখন সহায়তা করে। দয়া করে মনে রাখবেন যদি নতুন প্রক্রিয়া প্রেরণ সব কমান্ড লাইন আর্গুমেন্ট চাই, যে sys.argv[0]সম্ভবত একটি ফাংশন কল দিয়ে প্রতিস্থাপিত করা উচিত: subprocess.list2cmdline(sys.argv)

#! /usr/bin/env python3
import ctypes
import enum
import subprocess
import sys

# Reference:
# msdn.microsoft.com/en-us/library/windows/desktop/bb762153(v=vs.85).aspx


# noinspection SpellCheckingInspection
class SW(enum.IntEnum):
    HIDE = 0
    MAXIMIZE = 3
    MINIMIZE = 6
    RESTORE = 9
    SHOW = 5
    SHOWDEFAULT = 10
    SHOWMAXIMIZED = 3
    SHOWMINIMIZED = 2
    SHOWMINNOACTIVE = 7
    SHOWNA = 8
    SHOWNOACTIVATE = 4
    SHOWNORMAL = 1


class ERROR(enum.IntEnum):
    ZERO = 0
    FILE_NOT_FOUND = 2
    PATH_NOT_FOUND = 3
    BAD_FORMAT = 11
    ACCESS_DENIED = 5
    ASSOC_INCOMPLETE = 27
    DDE_BUSY = 30
    DDE_FAIL = 29
    DDE_TIMEOUT = 28
    DLL_NOT_FOUND = 32
    NO_ASSOC = 31
    OOM = 8
    SHARE = 26


def bootstrap():
    if ctypes.windll.shell32.IsUserAnAdmin():
        main()
    else:
       # noinspection SpellCheckingInspection
        hinstance = ctypes.windll.shell32.ShellExecuteW(
            None,
            'runas',
            sys.executable,
            subprocess.list2cmdline(sys.argv),
            None,
            SW.SHOWNORMAL
        )
        if hinstance <= 32:
            raise RuntimeError(ERROR(hinstance))


def main():
    # Your Code Here
    print(input('Echo: '))


if __name__ == '__main__':
    bootstrap()

4

এই প্রশ্নের স্বীকৃতিপ্রদান ছিল জিজ্ঞাসা বছর আগে, আমি মনে করি আরো একটি মার্জিত সমাধানে দেওয়া হয় GitHub তার মডিউল pywinutils ব্যবহার frmdstryr দ্বারা:

অংশ:

import pythoncom
from win32com.shell import shell,shellcon

def copy(src,dst,flags=shellcon.FOF_NOCONFIRMATION):
    """ Copy files using the built in Windows File copy dialog

    Requires absolute paths. Does NOT create root destination folder if it doesn't exist.
    Overwrites and is recursive by default 
    @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx for flags available
    """
    # @see IFileOperation
    pfo = pythoncom.CoCreateInstance(shell.CLSID_FileOperation,None,pythoncom.CLSCTX_ALL,shell.IID_IFileOperation)

    # Respond with Yes to All for any dialog
    # @see http://msdn.microsoft.com/en-us/library/bb775799(v=vs.85).aspx
    pfo.SetOperationFlags(flags)

    # Set the destionation folder
    dst = shell.SHCreateItemFromParsingName(dst,None,shell.IID_IShellItem)

    if type(src) not in (tuple,list):
        src = (src,)

    for f in src:
        item = shell.SHCreateItemFromParsingName(f,None,shell.IID_IShellItem)
        pfo.CopyItem(item,dst) # Schedule an operation to be performed

    # @see http://msdn.microsoft.com/en-us/library/bb775780(v=vs.85).aspx
    success = pfo.PerformOperations()

    # @see sdn.microsoft.com/en-us/library/bb775769(v=vs.85).aspx
    aborted = pfo.GetAnyOperationsAborted()
    return success is None and not aborted    

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


2

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

http://technet.microsoft.com/en-us/magazine/2008.06.elevation.aspx

আমি মনে করি আপনি এটি ব্যবহার করলে এটি 'অ্যালাইভেট পাইথন থাই স্ক্রিপ্ট। পিপি'র মতো দেখায় I


2

আপনি কোথাও এবং লক্ষ্য ব্যবহার হিসাবে শর্টকাট তৈরি করতে পারেন: অজগর thycript.py তারপরে বৈশিষ্ট্যগুলির অধীনে এবং প্রশাসনিক হিসাবে অ্যাডভান্সড নির্বাচন করুন।

যখন ব্যবহারকারী শর্টকাট কার্যকর করে তখন তাদের অ্যাপ্লিকেশনটি উন্নত করতে বলবে।


1

যদি আপনার স্ক্রিপ্টটিতে সর্বদা প্রশাসকের অধিকার প্রয়োজন হয় তবে:

runas /user:Administrator "python your_script.py"

15
সাবধান, উচ্চতা! = প্রশাসক হিসাবে চলছে
কুগেল

আমি অজগর থেকে নতুন ... আপনি কি আমাকে বলতে পারেন আমি এই কোডটি কোথায় রাখব?
রাহাত ইসলাম খান

@ রাহাতস্লামখান: কমান্ড প্রম্পট উইন্ডোটি খুলুন এবং এটি সেখানে রাখুন: কমান্ডটি your_script.pyপ্রশাসক ব্যবহারকারী হিসাবে চালিত হয়। আপনি @ কুগেলের মন্তব্য বুঝতে পেরেছেন তা নিশ্চিত করুন
jfs

1

জোরেঙ্কোর কাজের উপরের পরিবর্তনের ফলে উন্নীত প্রক্রিয়াটি একই কনসোলটি ব্যবহার করতে দেয় (তবে নীচে আমার মন্তব্য দেখুন):

def spawn_as_administrator():
    """ Spawn ourself with administrator rights and wait for new process to exit
        Make the new process use the same console as the old one.
          Raise Exception() if we could not get a handle for the new re-run the process
          Raise pywintypes.error() if we could not re-spawn
        Return the exit code of the new process,
          or return None if already running the second admin process. """
    #pylint: disable=no-name-in-module,import-error
    import win32event, win32api, win32process
    import win32com.shell.shell as shell
    if '--admin' in sys.argv:
        return None
    script = os.path.abspath(sys.argv[0])
    params = ' '.join([script] + sys.argv[1:] + ['--admin'])
    SEE_MASK_NO_CONSOLE = 0x00008000
    SEE_MASK_NOCLOSE_PROCESS = 0x00000040
    process = shell.ShellExecuteEx(lpVerb='runas', lpFile=sys.executable, lpParameters=params, fMask=SEE_MASK_NO_CONSOLE|SEE_MASK_NOCLOSE_PROCESS)
    hProcess = process['hProcess']
    if not hProcess:
        raise Exception("Could not identify administrator process to install drivers")
    # It is necessary to wait for the elevated process or else
    #  stdin lines are shared between 2 processes: they get one line each
    INFINITE = -1
    win32event.WaitForSingleObject(hProcess, INFINITE)
    exitcode = win32process.GetExitCodeProcess(hProcess)
    win32api.CloseHandle(hProcess)
    return exitcode

দুঃখিত আপনি যদি ইতিমধ্যে উন্নত হন তবে একই কনসোল বিকল্পটি (SEE_MASK_NO_CONSOLE) কেবলমাত্র কাজ করে। আমার খারাপ।
বারউইন

1

এটি বেশিরভাগ ক্ষেত্রে জোরেঙ্কোর উত্তরের একটি আপগ্রেড, যা উইন্ডোজের ফাঁকা জায়গাগুলির সাথে প্যারামিটার ব্যবহার করতে দেয় তবে লিনাক্সেও বেশ ভালভাবে কাজ করা উচিত :) এছাড়াও, cx_freeze বা py2exe নিয়ে কাজ করবে যেহেতু আমরা ব্যবহার করি না __file__তবে sys.argv[0]এক্সিকিউটেবল হিসাবে

import sys,ctypes,platform

def is_admin():
    try:
        return ctypes.windll.shell32.IsUserAnAdmin()
    except:
        raise False

if __name__ == '__main__':

    if platform.system() == "Windows":
        if is_admin():
            main(sys.argv[1:])
        else:
            # Re-run the program with admin rights, don't use __file__ since py2exe won't know about it
            # Use sys.argv[0] as script path and sys.argv[1:] as arguments, join them as lpstr, quoting each parameter or spaces will divide parameters
            lpParameters = ""
            # Litteraly quote all parameters which get unquoted when passed to python
            for i, item in enumerate(sys.argv[0:]):
                lpParameters += '"' + item + '" '
            try:
                ctypes.windll.shell32.ShellExecuteW(None, "runas", sys.executable, lpParameters , None, 1)
            except:
                sys.exit(1)
    else:
        main(sys.argv[1:])
আমাদের সাইট ব্যবহার করে, আপনি স্বীকার করেছেন যে আপনি আমাদের কুকি নীতি এবং গোপনীয়তা নীতিটি পড়েছেন এবং বুঝতে পেরেছেন ।
Licensed under cc by-sa 3.0 with attribution required.