पृष्ठभूमि प्रक्रिया का निकास कोड प्राप्त करें


130

मेरे पास मेरी मुख्य बॉर्न शेल स्क्रिप्ट से एक कमांड सीएमडी है जो हमेशा के लिए लेता है।

मैं स्क्रिप्ट को इस प्रकार बदलना चाहता हूं:

  1. एक पृष्ठभूमि प्रक्रिया ( CMD &) के रूप में समानांतर में कमांड सीएमडी चलाएं ।
  2. मुख्य स्क्रिप्ट में, प्रत्येक कुछ सेकंड में स्पॉन कमांड को मॉनिटर करने के लिए एक लूप होता है। लूप भी स्क्रिप्ट की प्रगति का संकेत देने के लिए कुछ संदेश गूँजता है।
  3. जब स्पॉन्ड कमांड समाप्त हो जाए तो लूप से बाहर निकलें।
  4. कैप्चर की गई प्रक्रिया और स्पॉन्ड प्रक्रिया के निकास कोड की रिपोर्ट करें।

क्या कोई मुझे यह पूरा करने के लिए संकेत दे सकता है?


1
...और विजेता है?
ट्रू यर

1
@TrueY .. बॉब ने सवाल पूछने के दिन से लॉग इन नहीं किया है। हम कभी भी पता करने की संभावना नहीं है!
घोटी

जवाबों:


127

1: बैश में, $!अंतिम पृष्ठभूमि प्रक्रिया का PID रखता है जिसे निष्पादित किया गया था। यह आपको बताएगा कि किस प्रक्रिया को मॉनिटर करना है, वैसे भी।

4: wait <n>पीआईडी ​​के साथ प्रक्रिया <n>पूरी होने तक प्रतीक्षा करता है (यह प्रक्रिया पूरी होने तक अवरुद्ध हो जाएगी, इसलिए आप इसे तब तक नहीं कॉल कर सकते हैं जब तक आप सुनिश्चित नहीं कर लें कि प्रक्रिया पूरी हो गई है), और फिर पूरी हुई प्रक्रिया का निकास कोड लौटाता है।

2, 3: psया ps | grep " $! "आपको बता सकता है कि क्या प्रक्रिया अभी भी चल रही है। यह आप पर निर्भर है कि आउटपुट को कैसे समझा जाए और यह तय किया जाए कि यह परिष्करण के कितने करीब है। ( ps | grepइडियट-प्रूफ नहीं है। यदि आपके पास समय है तो आप यह बताने के लिए अधिक मजबूत तरीके से आ सकते हैं कि क्या प्रक्रिया अभी भी चल रही है)

यहाँ एक कंकाल स्क्रिप्ट है:

# simulate a long process that will have an identifiable exit code
(sleep 15 ; /bin/false) &
my_pid=$!

while   ps | grep " $my_pid "     # might also need  | grep -v grep  here
do
    echo $my_pid is still in the ps output. Must still be running.
    sleep 3
done

echo Oh, it looks like the process is done.
wait $my_pid
# The variable $? always holds the exit code of the last command to finish.
# Here it holds the exit code of $my_pid, since wait exits with that code. 
my_status=$?
echo The exit status of the process was $my_status

15
ps -p $my_pid -o pid=grepकी जरूरत है।
अगली सूचना तक रोक दिया गया।

54
kill -0 $!यह बताने का एक बेहतर तरीका है कि क्या कोई प्रक्रिया अभी भी चल रही है। यह वास्तव में कोई संकेत नहीं भेजता है, केवल जाँचता है कि बाहरी प्रक्रियाओं के बजाय एक शेल-इन का उपयोग करके प्रक्रिया जीवित है। के रूप में man 2 killकहते हैं, "यदि sig 0 है, तो कोई संकेत नहीं भेजा जाता है, लेकिन त्रुटि जाँच अभी भी की जाती है; इसका उपयोग प्रक्रिया ID या प्रक्रिया समूह ID के अस्तित्व की जाँच के लिए किया जा सकता है।"
१।

14
kill -0यदि आपके पास चल रही प्रक्रिया के लिए सिग्नल भेजने की अनुमति नहीं है तो @ephemient गैर-शून्य वापस आ जाएगा। दुर्भाग्य से यह 1इस मामले और उस मामले दोनों में वापस आता है जहां प्रक्रिया मौजूद नहीं है। यह तब तक उपयोगी है जब तक कि आप इस प्रक्रिया के स्वामी नहीं हैं - जो आपके द्वारा बनाई गई प्रक्रियाओं के लिए भी हो सकता है यदि कोई उपकरण sudoशामिल है या यदि वे सेट्युइड हैं (और संभवतः निजी ड्रॉप करें)।
क्रेग रिंगर

13
waitचर में बाहर निकलने का कोड वापस नहीं करता है $?। यह केवल निकास कोड देता है और $?नवीनतम अग्रभूमि कार्यक्रम का निकास कोड है।
माइंडलेसरंगर

7
बहुत से लोगों ने मतदान किया kill -0। यहाँ SO से एक सहकर्मी की समीक्षा का संदर्भ है जो दिखाता है कि क्रेगिंगर की टिप्पणी वैध है: kill -0चल रही प्रक्रियाओं के लिए गैर-शून्य लौटेगी ... लेकिन ps -pकिसी भी चल रही प्रक्रिया के लिए हमेशा 0 वापस लौटेगी
ट्रेवर बॉयड स्मिथ

58

जब मुझे इसी तरह की आवश्यकता थी तो मैंने इसे हल किया:

# Some function that takes a long time to process
longprocess() {
        # Sleep up to 14 seconds
        sleep $((RANDOM % 15))
        # Randomly exit with 0 or 1
        exit $((RANDOM % 2))
}

pids=""
# Run five concurrent processes
for i in {1..5}; do
        ( longprocess ) &
        # store PID of process
        pids+=" $!"
done

# Wait for all processes to finish, will take max 14s
# as it waits in order of launch, not order of finishing
for p in $pids; do
        if wait $p; then
                echo "Process $p success"
        else
                echo "Process $p fail"
        fi
done

मुझे यह तरीका पसंद है।
केमिन झोउ

धन्यवाद! यह मुझे सबसे सरल दृष्टिकोण लगता है।
ल्यूक डेविस

4
यह समाधान आवश्यकता # 2 को संतुष्ट नहीं करता है: पृष्ठभूमि प्रक्रिया के अनुसार एक निगरानी पाश। waitस्क्रिप्ट के कारण (प्रत्येक) प्रक्रिया के बहुत अंत तक प्रतीक्षा करें।
DKroot

सरल और अच्छा दृष्टिकोण .. इस समाधान को काफी समय से खोज रहे हैं ..
संतोष कुमार अर्जुन

यह काम नहीं करता है .. या वह नहीं करता है जो आप चाहते हैं: यह पृष्ठभूमि वाली प्रक्रियाओं से बाहर निकलने की स्थिति की जांच नहीं करता है?
शंकु

10

एक पृष्ठभूमि वाले बच्चे की प्रक्रिया की पिड $ में संग्रहित है ! । आप सभी बच्चों की प्रक्रियाओं को एक सरणी, उदाहरण के लिए PIDS [] में स्टोर कर सकते हैं ।

wait [-n] [jobspec or pid …]

प्रतीक्षा करें जब तक कि प्रत्येक प्रक्रिया आईडी पिड या जॉब स्पेसिफिकेशन जॉबस्पेक द्वारा निर्दिष्ट बच्चे की प्रक्रिया समाप्त न हो जाए और अंतिम कमांड के एक्जिट स्टेटस का इंतजार करें। यदि नौकरी की युक्ति दी जाती है, तो नौकरी में सभी प्रक्रियाओं का इंतजार किया जाता है। यदि कोई तर्क नहीं दिया जाता है, तो वर्तमान में सभी सक्रिय बाल प्रक्रियाओं का इंतजार किया जाता है, और वापसी की स्थिति शून्य है। यदि एक विकल्प की आपूर्ति की जाती है, तो किसी भी नौकरी को समाप्त करने के लिए प्रतीक्षा करें और अपनी निकास स्थिति लौटाएं। यदि न तो जॉबस्पेक और न ही पीआईडी ​​शेल की सक्रिय बाल प्रक्रिया को निर्दिष्ट करता है, तो वापसी की स्थिति 127 है।

प्रतीक्षा आदेश का उपयोग करें आप सभी बाल प्रक्रियाओं को समाप्त करने के लिए इंतजार कर सकते हैं, इस बीच आप $ के माध्यम से प्रत्येक बच्चे की प्रक्रियाओं की निकास स्थिति प्राप्त कर सकते हैं ? और स्थिति में स्टोर की स्थिति [] । फिर आप स्टेटस के आधार पर कुछ कर सकते हैं।

मैंने निम्नलिखित 2 समाधानों की कोशिश की है और वे अच्छी तरह से चलते हैं। solution01 अधिक संक्षिप्त है, जबकि solution02 थोड़ा जटिल है।

solution01

#!/bin/bash

# start 3 child processes concurrently, and store each pid into array PIDS[].
process=(a.sh b.sh c.sh)
for app in ${process[@]}; do
  ./${app} &
  PIDS+=($!)
done

# wait for all processes to finish, and store each process's exit code into array STATUS[].
for pid in ${PIDS[@]}; do
  echo "pid=${pid}"
  wait ${pid}
  STATUS+=($?)
done

# after all processed finish, check their exit codes in STATUS[].
i=0
for st in ${STATUS[@]}; do
  if [[ ${st} -ne 0 ]]; then
    echo "$i failed"
  else
    echo "$i finish"
  fi
  ((i+=1))
done

solution02

#!/bin/bash

# start 3 child processes concurrently, and store each pid into array PIDS[].
i=0
process=(a.sh b.sh c.sh)
for app in ${process[@]}; do
  ./${app} &
  pid=$!
  PIDS[$i]=${pid}
  ((i+=1))
done

# wait for all processes to finish, and store each process's exit code into array STATUS[].
i=0
for pid in ${PIDS[@]}; do
  echo "pid=${pid}"
  wait ${pid}
  STATUS[$i]=$?
  ((i+=1))
done

# after all processed finish, check their exit codes in STATUS[].
i=0
for st in ${STATUS[@]}; do
  if [[ ${st} -ne 0 ]]; then
    echo "$i failed"
  else
    echo "$i finish"
  fi
  ((i+=1))
done

मैंने कोशिश की है और साबित किया है कि यह अच्छी तरह से चलता है। आप मेरे स्पष्टीकरण को कोड में पढ़ सकते हैं।
टेरी

कृपया पढ़ें " मैं एक अच्छा उत्तर कैसे लिखूं? " आपको निम्नलिखित जानकारी कहां मिलेगी: ... अपने उत्तर में किसी भी सीमा, धारणा या सरलीकरण का उल्लेख करने का प्रयास करें। ब्रेवीटी स्वीकार्य है, लेकिन फुलर स्पष्टीकरण बेहतर हैं। आप उत्तर इसलिए स्वीकार्य हैं लेकिन यदि आपके पास समस्या और समाधान के बारे में विस्तार से बताया जा सकता है, तो आपके पास उठाव होने की बेहतर संभावना है। :-)
नोएल विडमर

1
pid=$!; PIDS[$i]=${pid}; ((i+=1))अधिक बस के रूप में लिखा जा सकता है, PIDS+=($!)जो केवल अनुक्रमण या pid के लिए एक अलग चर का उपयोग किए बिना सरणी में संलग्न करता है। यही बात STATUSसरणी पर लागू होती है ।
कोडवर्ड

1
@codeforester, आपके sugesstion के लिए धन्यवाद, मैंने अपने inital कोड को solution01 में बदल दिया है, यह अधिक संक्षिप्त लगता है।
टेरी

यही बात अन्य स्थानों पर भी लागू होती है जहाँ आप चीजों को एक सरणी में जोड़ रहे हैं।
कोडवर्ड

8

जैसा कि मैंने देखा कि लगभग सभी उत्तर psपृष्ठभूमि प्रक्रिया की स्थिति को प्रदूषित करने के लिए बाहरी उपयोगिताओं (ज्यादातर ) का उपयोग करते हैं। SIGCHLD सिग्नल को पकड़ने के लिए एक और अधिक निराला समाधान है। सिग्नल हैंडलर में यह जांचना पड़ता है कि किस बच्चे की प्रक्रिया को रोक दिया गया था। यह kill -0 <PID>बिल्ट-इन (यूनिवर्सल) या /proc/<PID>डायरेक्टरी के अस्तित्व (लिनक्स विशिष्ट) की जाँच करके या jobsबिल्ट-इन का उपयोग करके किया जा सकता हैविशिष्ट। jobs -lपिड की भी रिपोर्ट करता है। इस स्थिति में आउटपुट का तीसरा क्षेत्र रोक दिया जा सकता है। )।

यहाँ मेरा उदाहरण है।

लॉन्च की गई प्रक्रिया को कहा जाता है loop.sh। यह -xएक तर्क के रूप में स्वीकार या एक संख्या है। के लिए -xबाहर निकलने के कोड 1. एक नंबर यह संख्या * 5 सेकंड इंतजार कर रहा है के लिए के साथ बाहर निकलता है। हर 5 सेकंड में यह अपने PID को प्रिंट करता है।

लॉन्चर प्रक्रिया कहा जाता है launch.sh:

#!/bin/bash

handle_chld() {
    local tmp=()
    for((i=0;i<${#pids[@]};++i)); do
        if [ ! -d /proc/${pids[i]} ]; then
            wait ${pids[i]}
            echo "Stopped ${pids[i]}; exit code: $?"
        else tmp+=(${pids[i]})
        fi
    done
    pids=(${tmp[@]})
}

set -o monitor
trap "handle_chld" CHLD

# Start background processes
./loop.sh 3 &
pids+=($!)
./loop.sh 2 &
pids+=($!)
./loop.sh -x &
pids+=($!)

# Wait until all background processes are stopped
while [ ${#pids[@]} -gt 0 ]; do echo "WAITING FOR: ${pids[@]}"; sleep 2; done
echo STOPPED

अधिक विवरण के लिए देखें: बैश स्क्रिप्ट से एक प्रक्रिया शुरू करना विफल रहा


1
चूंकि हम बैश के बारे में बात कर रहे हैं, इसलिए लूप के लिए लिखा जा सकता है: for i in ${!pids[@]};पैरामीटर विस्तार का उपयोग करना।
प्लाज़्माबिनटॉन्ग

7
#/bin/bash

#pgm to monitor
tail -f /var/log/messages >> /tmp/log&
# background cmd pid
pid=$!
# loop to monitor running background cmd
while :
do
    ps ax | grep $pid | grep -v grep
    ret=$?
    if test "$ret" != "0"
    then
        echo "Monitored pid ended"
        break
    fi
    sleep 5

done

wait $pid
echo $?

2
यहाँ एक चाल से बचने के लिए है grep -v। आप खोज को लाइन की शुरुआत तक सीमित grep '^'$pidकर सकते हैं : प्लस, आप ps p $pid -o pid=वैसे भी कर सकते हैं । इसके अलावा, tail -fसमाप्त करने के लिए जब तक आप इसे मारने तो मैं यह (कम से कम बिना कि उनका कहना है) इस डेमो के लिए एक बहुत अच्छा तरीका है नहीं लगता है नहीं जा रहा है। आप अपने के उत्पादन में पुनर्निर्देशित करने के लिए चाहते हो सकता है psके लिए आदेश /dev/nullया यह हर यात्रा में स्क्रीन पर पहुंच जाएंगे। आपके exitकारणों को waitछोड़ दिया जाना चाहिए - यह संभवतः होना चाहिए break। लेकिन while/ psऔर waitनिरर्थक नहीं हैं?
अगली सूचना तक रोक दिया गया।

5
हर कोई क्यों भूल जाता है kill -0 $pid? यह वास्तव में कोई संकेत नहीं भेजता है, केवल जाँचता है कि बाहरी प्रक्रियाओं के बजाय एक शेल-इन का उपयोग करके प्रक्रिया जीवित है।
१17

3
क्योंकि आप केवल अपनी खुद की एक प्रक्रिया को मार सकते हैं:bash: kill: (1) - Operation not permitted
misant.info

2
पाश बेमानी है। बस इंतज़ार करें। कम कोड => कम किनारे के मामले।
ब्रिस गाबिन

@ ब्रिस गेबिन निगरानी लूप प्रश्न की # 2 आवश्यकता है
DKroot

5

मैं आपके दृष्टिकोण को थोड़ा बदल दूंगा। हर कुछ सेकंड की जाँच करने के बजाय अगर कमांड अभी भी जीवित है और एक संदेश की रिपोर्ट कर रहा है, तो एक और प्रक्रिया है जो हर कुछ सेकंड में रिपोर्ट करती है कि कमांड अभी भी चल रहा है और फिर कमांड खत्म होने पर उस प्रक्रिया को मार दें। उदाहरण के लिए:

#! / Bin / श

cmd () {नींद 5; बाहर निकलें 24; }

cmd & # रनिंग की लंबी प्रक्रिया
पीआईडी ​​= $! # रिकॉर्ड पीआईडी

# एक ऐसी प्रक्रिया को अंजाम दें जो यह बताती है कि कमान अभी भी चल रही है
जबकि गूंज "$ (दिनांक): $ पीड अभी भी चल रहा है"; सो जाओ 1; किया हुआ &
echoer = $!

# प्रक्रिया खत्म होने पर रिपोर्टर को मारने के लिए एक जाल बिछाएं
ट्रैप 'किल $ गूंज' 0

# प्रक्रिया समाप्त होने तक प्रतीक्षा करें
अगर इंतजार $ pid; फिर
    गूंज "cmd सफल"
अन्य
    इको "cmd विफल !! ($ लौटा?)"
फाई

महान टेम्पलेट, साझा करने के लिए धन्यवाद! मेरा मानना ​​है कि जाल के बजाय, हम यह भी कर सकते हैं while kill -0 $pid 2> /dev/null; do X; done, आशा है कि यह भविष्य में किसी और के लिए उपयोगी होगा जो इस संदेश को पढ़ता है;)
19 '13

3

हमारी टीम को एक दूरस्थ SSH-निष्पादित स्क्रिप्ट की समान आवश्यकता थी जो 25 मिनट की निष्क्रियता के बाद बाहर हो रही थी। यहाँ मॉनिटरिंग लूप के साथ एक समाधान है जो हर सेकंड की पृष्ठभूमि की प्रक्रिया की जाँच करता है, लेकिन निष्क्रियता की समय सीमा को दबाने के लिए हर 10 मिनट में छपाई करता है।

long_running.sh & 
pid=$!

# Wait on a background job completion. Query status every 10 minutes.
declare -i elapsed=0
# `ps -p ${pid}` works on macOS and CentOS. On both OSes `ps ${pid}` works as well.
while ps -p ${pid} >/dev/null; do
  sleep 1
  if ((++elapsed % 600 == 0)); then
    echo "Waiting for the completion of the main script. $((elapsed / 60))m and counting ..."
  fi
done

# Return the exit code of the terminated background process. This works in Bash 4.4 despite what Bash docs say:
# "If neither jobspec nor pid specifies an active child process of the shell, the return status is 127."
wait ${pid}

2

ऊपर दिए गए समाधानों के समान एक सरल उदाहरण। इसके लिए किसी प्रक्रिया आउटपुट की निगरानी की आवश्यकता नहीं है। अगला उदाहरण आउटपुट का पालन करने के लिए पूंछ का उपयोग करता है।

$ echo '#!/bin/bash' > tmp.sh
$ echo 'sleep 30; exit 5' >> tmp.sh
$ chmod +x tmp.sh
$ ./tmp.sh &
[1] 7454
$ pid=$!
$ wait $pid
[1]+  Exit 5                  ./tmp.sh
$ echo $?
5

प्रक्रिया आउटपुट का पालन करने के लिए पूंछ का उपयोग करें और प्रक्रिया पूरी होने पर छोड़ दें।

$ echo '#!/bin/bash' > tmp.sh
$ echo 'i=0; while let "$i < 10"; do sleep 5; echo "$i"; let i=$i+1; done; exit 5;' >> tmp.sh
$ chmod +x tmp.sh
$ ./tmp.sh
0
1
2
^C
$ ./tmp.sh > /tmp/tmp.log 2>&1 &
[1] 7673
$ pid=$!
$ tail -f --pid $pid /tmp/tmp.log
0
1
2
3
4
5
6
7
8
9
[1]+  Exit 5                  ./tmp.sh > /tmp/tmp.log 2>&1
$ wait $pid
$ echo $?
5

1

एक और उपाय है कि प्रोक्योर फाइलसिस्टम के माध्यम से प्रक्रियाओं की निगरानी की जाए (पीएस / जीआरईपी कॉम्बो की तुलना में सुरक्षित); जब आप एक प्रक्रिया शुरू करते हैं, तो इसमें / proc / $ pid में एक समान फ़ोल्डर होता है, इसलिए समाधान हो सकता है

#!/bin/bash
....
doSomething &
local pid=$!
while [ -d /proc/$pid ]; do # While directory exists, the process is running
    doSomethingElse
    ....
else # when directory is removed from /proc, process has ended
    wait $pid
    local exit_status=$?
done
....

अब आप $ exit_status वैरिएबल का उपयोग कर सकते हैं जो आपको पसंद है।


काम नहीं करता है? Syntax error: "else" unexpected (expecting "done")
benjaoming

1

इस पद्धति के साथ, आपकी स्क्रिप्ट को पृष्ठभूमि प्रक्रिया के लिए इंतजार नहीं करना पड़ेगा, आपको केवल बाहर निकलने की स्थिति के लिए एक अस्थायी फ़ाइल की निगरानी करनी होगी।

FUNCmyCmd() { sleep 3;return 6; };

export retFile=$(mktemp); 
FUNCexecAndWait() { FUNCmyCmd;echo $? >$retFile; }; 
FUNCexecAndWait&

अब, आपकी स्क्रिप्ट कुछ और भी कर सकती है, जबकि आपको सिर्फ रिटाइल की सामग्री की निगरानी रखनी होगी (इसमें कोई अन्य जानकारी भी हो सकती है जिसे आप बाहर निकलने के समय की तरह चाहते हैं)।

पुनश्च: btw, मैं बकवास में सोच कोडित


0

यह आपके प्रश्न से परे हो सकता है, हालांकि यदि आप समय की प्रक्रियाओं के बारे में चिंतित हैं, तो आप समय के अंतराल के बाद चल रही पृष्ठभूमि प्रक्रियाओं की स्थिति की जांच करने में रुचि ले सकते हैं। यह जांचना आसान है कि कौन से बच्चे पीआईडी ​​का उपयोग कर रहे हैंpgrep -P $$ , हालांकि मैं उन पीआईडी ​​की निकास स्थिति की जांच करने के लिए निम्नलिखित समाधान के साथ आया हूं जो पहले ही समाप्त हो चुके हैं:

cmd1() { sleep 5; exit 24; }
cmd2() { sleep 10; exit 0; }

pids=()
cmd1 & pids+=("$!")
cmd2 & pids+=("$!")

lasttimeout=0
for timeout in 2 7 11; do
  echo -n "interval-$timeout: "
  sleep $((timeout-lasttimeout))

  # you can only wait on a pid once
  remainingpids=()
  for pid in ${pids[*]}; do
     if ! ps -p $pid >/dev/null ; then
        wait $pid
        echo -n "pid-$pid:exited($?); "
     else
        echo -n "pid-$pid:running; "
        remainingpids+=("$pid")
     fi
  done
  pids=( ${remainingpids[*]} )

  lasttimeout=$timeout
  echo
done

कौन से आउटपुट:

interval-2: pid-28083:running; pid-28084:running; 
interval-7: pid-28083:exited(24); pid-28084:running; 
interval-11: pid-28084:exited(0); 

नोट: $pidsयदि आप चाहें तो चीजों को सरल बनाने के लिए आप सरणी के बजाय एक स्ट्रिंग चर में बदल सकते हैं ।


0

मेरा समाधान एक निगरानी पाइप के लिए स्थिति को पारित करने के लिए एक अनाम पाइप का उपयोग करना था। वहाँ कोई अस्थायी फ़ाइलें स्थिति का आदान-प्रदान करने के लिए उपयोग किया जाता है ताकि सफाई के लिए कुछ भी नहीं। यदि आप पृष्ठभूमि नौकरियों की संख्या के बारे में अनिश्चित थे तो ब्रेक की स्थिति हो सकती है [ -z "$(jobs -p)" ]

#!/bin/bash

exec 3<> <(:)

{ sleep 15 ; echo "sleep/exit $?" >&3 ; } &

while read -u 3 -t 1 -r STAT CODE || STAT="timeout" ; do
    echo "stat: ${STAT}; code: ${CODE}"
    if [ "${STAT}" = "sleep/exit" ] ; then
        break
    fi
done
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.