लिनक्स शेल स्क्रिप्ट से कैसे निपटता है?


22

इस प्रश्न के लिए, आइए एक बैश शेल स्क्रिप्ट पर विचार करें, हालांकि यह प्रश्न सभी प्रकार की शेल स्क्रिप्ट पर लागू होना चाहिए।

जब कोई किसी शेल स्क्रिप्ट को निष्पादित करता है, तो क्या लिनक्स सभी स्क्रिप्ट को एक बार में लोड करता है (शायद मेमोरी में) या क्या यह स्क्रिप्ट कमांड को एक-एक करके पढ़ता है (लाइन द्वारा लाइन)?

दूसरे शब्दों में, यदि मैं किसी शेल स्क्रिप्ट को निष्पादित करता हूं और निष्पादन पूरा होने से पहले इसे हटा देता हूं, तो क्या निष्पादन समाप्त हो जाएगा या क्या यह जारी रहेगा?


3
कोशिश करो। (यह जारी रहेगा।)
devnull

1
@ देवनुल वास्तव में यहां एक दिलचस्प सवाल है। दी गई, यह जारी रखने के लिए कि क्या यह परीक्षण के लिए तुच्छ है या नहीं, लेकिन बाइनरी फ़ाइलों (जो कि मेमोरी में लोड होती हैं) और एक शेबंग लाइन के साथ स्क्रिप्ट, या शेबंग लाइन के बिना स्क्रिप्ट के बीच अंतर हैं।
terdon

1
आप में रुचि हो सकती इस उत्तर
terdon

23
अपने वास्तविक इरादे के उद्देश्य के लिए, इसके निष्पादन के दौरान शेल स्क्रिप्ट को हटाने से, यह कोई फर्क नहीं पड़ता कि यह एक बार में पढ़ा जाए या लाइन-बाय-लाइन। यूनिक्स में, एक इनोड वास्तव में नष्ट नहीं होता है (भले ही किसी भी निर्देशिका से इसका कोई लिंक न हो) जब तक कि अंतिम खुली फ़ाइल बंद न हो जाए। दूसरे शब्दों में, यदि आपका शेल निष्पादन के दौरान लाइन द्वारा शेल स्क्रिप्ट लाइन में पढ़ता है, तो भी इसे हटाना सुरक्षित है। एकमात्र अपवाद यह है कि यदि आपका शेल वह प्रकार है जो हर बार शेल स्क्रिप्ट को बंद और फिर से खोल देता है, लेकिन यदि ऐसा होता है, तो आपको बहुत बड़ी (सुरक्षा) समस्याएं हैं।
क्रिस जस्टर-यंग

जवाबों:


33

यदि आप उपयोग करते हैं straceतो आप देख सकते हैं कि एक शेल स्क्रिप्ट को कैसे चलाया जाता है।

उदाहरण

कहो कि मेरे पास यह शेल स्क्रिप्ट है।

$ cat hello_ul.bash 
#!/bin/bash

echo "Hello Unix & Linux!"

इसका उपयोग करके चल रहा है strace:

$ strace -s 2000 -o strace.log ./hello_ul.bash
Hello Unix & Linux!
$

strace.logफ़ाइल के अंदर देखने से निम्नलिखित का पता चलता है।

...
open("./hello_ul.bash", O_RDONLY)       = 3
ioctl(3, SNDCTL_TMR_TIMEBASE or SNDRV_TIMER_IOCTL_NEXT_DEVICE or TCGETS, 0x7fff0b6e3330) = -1 ENOTTY (Inappropriate ioctl for device)
lseek(3, 0, SEEK_CUR)                   = 0
read(3, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 80) = 40
lseek(3, 0, SEEK_SET)                   = 0
getrlimit(RLIMIT_NOFILE, {rlim_cur=1024, rlim_max=4*1024}) = 0
fcntl(255, F_GETFD)                     = -1 EBADF (Bad file descriptor)
dup2(3, 255)                            = 255
close(3)     
...

एक बार फ़ाइल में पढ़ने के बाद, इसे निष्पादित किया जाता है:

...
read(255, "#!/bin/bash\n\necho \"Hello Unix & Linux!\"\n", 40) = 40
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
fstat(1, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 3), ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fc0b38ba000
write(1, "Hello Unix & Linux!\n", 20)   = 20
rt_sigprocmask(SIG_BLOCK, NULL, [], 8)  = 0
read(255, "", 40)                       = 0
exit_group(0)                           = ?

उपरोक्त में हम स्पष्ट रूप से देख सकते हैं कि पूरी स्क्रिप्ट एक ही इकाई के रूप में पढ़ी जा रही है, और उसके बाद वहां निष्पादित की जाती है। तो यह कम से कम बाश के मामले में "दिखाई" देगा कि यह फ़ाइल को पढ़ता है, और फिर इसे निष्पादित करता है। तो आपको लगता है कि आप स्क्रिप्ट को संपादित कर सकते हैं जबकि यह चल रहा है?

नोट: हालांकि, नहीं! यह समझने के लिए पढ़ें कि आपको रनिंग स्क्रिप्ट फ़ाइल में गड़बड़ क्यों नहीं करनी चाहिए।

अन्य दुभाषियों के बारे में क्या?

लेकिन आपका सवाल थोड़ा हटकर है। यह लिनक्स नहीं है जो आवश्यक रूप से फ़ाइल की सामग्री को लोड कर रहा है, यह सामग्री को लोड करने वाला दुभाषिया है, इसलिए यह वास्तव में ऊपर है कि दुभाषिया को कैसे लागू किया जाता है चाहे वह एक बार में पूरी तरह से या ब्लॉक या लाइनों में फ़ाइल लोड करता है।

तो हम फ़ाइल को संपादित क्यों नहीं कर सकते?

यदि आप एक बहुत बड़ी स्क्रिप्ट का उपयोग करते हैं, तो आप देखेंगे कि उपरोक्त परीक्षण थोड़ा भ्रामक है। वास्तव में अधिकांश दुभाषियों ने अपनी फ़ाइलों को ब्लॉक में लोड किया है। यह बहुत से यूनिक्स उपकरणों के साथ मानक है जहां वे एक फाइल के ब्लॉक को लोड करते हैं, इसे प्रोसेस करते हैं, और फिर दूसरे ब्लॉक को लोड करते हैं। आप इस व्यवहार को इस U & L Q & A के साथ देख सकते हैं, जिसे मैंने कुछ समय पहले लिखा था grep, जिसका शीर्षक है: प्रत्येक बार grep / egrep कितना पाठ उपभोग करता है?

उदाहरण

कहें कि हम निम्नलिखित शेल स्क्रिप्ट बनाते हैं।

$ ( 
    echo '#!/bin/bash'; 
    for i in {1..100000}; do printf "%s\n" "echo \"$i\""; done 
  ) > ascript.bash;
$ chmod +x ascript.bash

इस फ़ाइल में परिणाम:

$ ll ascript.bash 
-rwxrwxr-x. 1 saml saml 1288907 Mar 23 18:59 ascript.bash

जिसमें निम्न प्रकार की सामग्री शामिल है:

$ head -3 ascript.bash ; echo "..."; tail -3 ascript.bash 
#!/bin/bash
echo "1"
echo "2"
...
echo "99998"
echo "99999"
echo "100000"

अब जब आप इसे उसी तकनीक का उपयोग करके ऊपर से चलाते हैं strace:

$ strace -s 2000 -o strace_ascript.log ./ascript.bash
...    
read(255, "#!/bin/bash\necho \"1\"\necho \"2\"\necho \"3\"\necho \"4\"\necho \"5\"\necho \"6\"\necho \"7\"\necho \"8\"\necho \"9\"\necho \"10\"\necho 
...
...
\"181\"\necho \"182\"\necho \"183\"\necho \"184\"\necho \"185\"\necho \"186\"\necho \"187\"\necho \"188\"\necho \"189\"\necho \"190\"\necho \""..., 8192) = 8192

आप देखेंगे कि फ़ाइल को 8KB वेतन वृद्धि पर पढ़ा जा रहा है, इसलिए बैश और अन्य गोले संभवतः किसी फ़ाइल को उसकी संपूर्णता में लोड नहीं करेंगे, बल्कि वे उन्हें ब्लॉक में पढ़ते हैं।

संदर्भ


@terdon - हाँ मुझे याद है कि Q & A को पहले देखना।
slm

5
40-बाइट स्क्रिप्ट के साथ, निश्चित रूप से, यह एक ब्लॉक में पढ़ा जाता है। > 8kB स्क्रिप्ट के साथ प्रयास करें।
गिलेस एसओ- बुराई को रोकना '

मैंने कभी कोशिश नहीं की, लेकिन मुझे लगता है कि फ़ाइलों को हटाना वास्तव में तब तक नहीं किया जाता है जब तक कि सभी प्रक्रियाएं हटाए गए फ़ाइल से जुड़े फ़ाइल डिस्क्रिप्टर को बंद न कर दें, इसलिए बैश को हटाए गए फ़ाइल से पढ़ना जारी रख सकते हैं।
फरीद नूरी नेस्तत

@ गिल्स - हां मैंने एक उदाहरण जोड़ा है, यह हो रहा है।
slm

2
यह व्यवहार संस्करण-निर्भर है। मैंने बैश संस्करण 3.2.51 (1) -release के साथ परीक्षण किया, और पाया कि यह वर्तमान लाइन के पिछले बफर नहीं करता था (यह स्टैकओवरफ़्लो उत्तर देखें )।
गॉर्डन डेविसन

11

यह ओएस आश्रित की तुलना में अधिक शेल पर निर्भर है।

संस्करण के आधार पर, ksh8k या 64k बाइट्स ब्लॉक द्वारा मांग पर स्क्रिप्ट पढ़ें।

bashलाइन द्वारा स्क्रिप्ट लाइन पढ़ें। हालाँकि, दी गई तथ्य रेखाएँ मनमानी हो सकती हैं, यह अगली पंक्ति की शुरुआत से पार्स करने तक हर बार 8176 बाइट्स को पढ़ता है।

यह साधारण निर्माणों के लिए है, यानी सादे कमांड का एक सूट।

यदि शेल स्ट्रक्चर्ड कमांड का उपयोग किया जाता है ( एक मामला जिसे मानने के लिए स्वीकार किया गया उत्तर याद आता है ) जैसे for/do/doneलूप, एक case/esacस्विच, एक दस्तावेज़, कोष्ठक द्वारा संलग्न उपखंड, एक फ़ंक्शन परिभाषा, आदि और उपरोक्त के किसी भी संयोजन, शेल दुभाषियों को पढ़ता है। निर्माण के अंत में पहले यह सुनिश्चित करने के लिए कि कोई सिंटैक्स त्रुटि नहीं है।

यह कुछ हद तक अक्षम है क्योंकि एक ही कोड को एक बार फिर से बड़ी संख्या में पढ़ा जा सकता है लेकिन इस सामग्री को सामान्य रूप से कैश किया जाता है।

शेल इंटरप्रेटर जो कुछ भी है, वह शेल स्क्रिप्ट को संशोधित करने के लिए बहुत नासमझ है, जबकि इसे निष्पादित किया जा रहा है क्योंकि शेल स्क्रिप्ट के किसी भी हिस्से को फिर से पढ़ने के लिए स्वतंत्र है और इससे सिंक से बाहर होने पर अप्रत्याशित सिंटैक्स त्रुटियां हो सकती हैं।

यह भी ध्यान दें कि बैश एक विभाजन उल्लंघन के साथ दुर्घटनाग्रस्त हो सकता है जब यह बड़े पैमाने पर स्क्रिप्ट निर्माण को स्टोर करने में असमर्थ होता है ksh93 त्रुटिपूर्ण पढ़ सकता है।


7

यह इस बात पर निर्भर करता है कि स्क्रिप्ट चलाने वाला दुभाषिया कैसे काम करता है। सभी कर्नेल करता है कि फ़ाइल को निष्पादित करने के लिए नोटिस करना शुरू होता है #!, अनिवार्य रूप से एक कार्यक्रम के रूप में शेष रेखा को चलाता है और इसे निष्पादन योग्य को तर्क के रूप में देता है। अगर वहाँ सूचीबद्ध दुभाषिया उस फाइल लाइन को लाइन से पढ़ता है (जैसा कि इंटरैक्टिव गोले आप जो टाइप करते हैं), वही आपको मिलता है (लेकिन मल्टी-लाइन लूप संरचनाएं दोहराई जाती हैं और दोहराई जाती हैं); यदि दुभाषिया फ़ाइल को मेमोरी में खिसकाता है, तो उसे संसाधित करता है (शायद इसे एक मध्यवर्ती प्रतिनिधित्व के लिए संकलित करता है, जैसे पर्ल और पायटन करते हैं) फ़ाइल को निष्पादित करने से पहले पूरी तरह से पढ़ा जाता है।

यदि आप इस बीच फ़ाइल को हटाते हैं, तो फ़ाइल तब तक नहीं हटाई जाती है जब तक कि दुभाषिया इसे बंद नहीं करता है (हमेशा की तरह, अंतिम संदर्भ के दौरान फाइलें चली जाती हैं, यह एक निर्देशिका प्रविष्टि या इसे खुला रखने वाली प्रक्रिया हो) विघटन।


4

'X' फ़ाइल:

cat<<'dog' >xyzzy
LANG=C
T=`tty`
( sleep 2 ; ls -l xyzzy >$T ) &
( sleep 4 ; rm -v xyzzy >$T ) &
( sleep 4 ; ls -l xyzzy >$T ) &
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
echo alive. ; sleep 1
dog

sh xyzzy

दौड़:

~/wrk/tmp$ sh x
alive.
alive.
alive.
-rw-r--r-- 1 yeti yeti 287 Mar 23 16:57 xyzzy
alive.
removed `xyzzy'
ls: cannot access xyzzy: No such file or directory
alive.
alive.
alive.
alive.
~/wrk/tmp$ _

IIRC एक फ़ाइल तब तक नहीं हटाई जाती है जब तक कोई प्रक्रिया उसे खोलती रहती है। विलोपन केवल दिए गए DIRENT को निकालता है।

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.