क्यों "पीएस कुल्हाड़ी" बिना "#!" हैडर?


13

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

# foo.sh

while true; do sleep 1; done

... मैं इसका उपयोग नहीं कर पा रहा हूँ ps ax:

>./foo.sh

// In a separate shell:
>ps ax | grep foo.sh
21110 pts/3    S+     0:00 grep --color=auto foo.sh

... लेकिन अगर मैं सिर्फ #!स्क्रिप्ट में कॉमन " " हेडर जोड़ता हूं ...

#! /usr/bin/bash
# foo.sh

while true; do sleep 1; done

... फिर स्क्रिप्ट उसी psकमांड से खोजने योग्य हो जाती है ...

>./foo.sh

// In a separate shell:
>ps ax | grep foo.sh
21319 pts/43   S+     0:00 /usr/bin/bash ./foo.sh
21324 pts/3    S+     0:00 grep --color=auto foo.sh

ऐसा क्यों है?
यह एक संबंधित प्रश्न हो सकता है: मुझे लगा कि " #" केवल एक टिप्पणी उपसर्ग था, और यदि ऐसा है तो " #! /usr/bin/bash" स्वयं एक टिप्पणी से ज्यादा कुछ नहीं है। लेकिन क्या " #!" केवल एक टिप्पणी से कुछ अधिक महत्व रखता है ?


यूनिक्स आप क्या उपयोग कर रहे हैं?
Kusalananda

@ कुसलानंद - लिनक्स लाइनबॉक्स 3.11.10-301.fc20.x86_64 # 1 एसएमपी थू दिसंबर 5 14:01:17 यूटीसी 2013 x86_64 x86_64 x86_64 GNU / लिनक्स
स्टोनथ्रो

जवाबों:


13

जब वर्तमान इंटरेक्टिव शेल होता है bash, और आप स्क्रिप्ट को बिना- #!लाइन के bashचलाते हैं , तो स्क्रिप्ट चलाएगा। प्रक्रिया ps axआउटपुट में ही दिखेगी bash

$ cat foo.sh
# foo.sh

echo "$BASHPID"
while true; do sleep 1; done

$ ./foo.sh
55411

दूसरे टर्मिनल में:

$ ps -p 55411
  PID TT  STAT       TIME COMMAND
55411 p2  SN+     0:00.07 bash

सम्बंधित:


संबंधित अनुभाग bashमैनुअल बनाते हैं :

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

यदि प्रोग्राम एक फ़ाइल है जिसके साथ शुरुआत होती है #!, तो पहली पंक्ति का शेष प्रोग्राम के लिए दुभाषिया निर्दिष्ट करता है। शेल ऑपरेटिंग सिस्टम पर निर्दिष्ट दुभाषिया को निष्पादित करता है जो इस निष्पादन योग्य प्रारूप को स्वयं नहीं संभालता है। [...]

इसका मतलब यह है कि ./foo.shकमांड लाइन पर चलने पर, जब foo.shएक #!इनलाइन नहीं होती है , तो फाइल में कमांड्स को एक सबहेल्ड में चलाने के समान है, जैसे कि

$ ( echo "$BASHPID"; while true; do sleep 1; done )

#!उदाहरण के लिए इंगित करने वाली उचित- पंक्ति के साथ/bin/bash , यह ऐसा कर रहा है

$ /bin/bash foo.sh

मुझे लगता है कि मैं अनुसरण करता हूं, लेकिन आप जो कहते हैं वह दूसरे मामले में भी सच है: बैश भी दूसरे मामले में स्क्रिप्ट चलाता है, जैसा कि देखा जा सकता है जब psस्क्रिप्ट को " /usr/bin/bash ./foo.sh" के रूप में चलता है । तो पहले मामले में, जैसा कि आप कहते हैं, बैश स्क्रिप्ट चलाएगा, लेकिन क्या उस स्क्रिप्ट को दूसरे केस की तरह फोर्स्ड बैश निष्पादन योग्य होने के लिए "पास" करने की आवश्यकता नहीं है? (और यदि ऐसा है, तो मुझे लगता है कि यह पाइप के साथ
मिलाने

@StoneThrow अपडेटेड उत्तर देखें।
Kusalananda

"... सिवाय इसके कि आपको एक नई प्रक्रिया मिलती है" - ठीक है, आपको किसी भी तरह से एक नई प्रक्रिया मिलती है, सिवाय इसके कि $$अभी भी उप-मामले ( echo $BASHPID/ bash -c 'echo $PPID') में पुराने एक पर इंगित करता है ।
माइकल होमर

@ मिचेलहोमर आह, इसके लिए धन्यवाद! अपडेट करूंगी।
Kusalananda

12

जब कोई शेल स्क्रिप्ट शुरू होती है #!, तो वह पहली पंक्ति एक टिप्पणी होती है जहां तक ​​शेल का संबंध है। हालाँकि पहले दो अक्षर सिस्टम के दूसरे भाग के लिए सार्थक हैं: कर्नेल। दो पात्रों #!को एक शबंग कहा जाता है । शेबंग की भूमिका को समझने के लिए, आपको यह समझने की आवश्यकता है कि किसी प्रोग्राम को कैसे निष्पादित किया जाता है।

फ़ाइल से प्रोग्राम को निष्पादित करने के लिए कर्नेल से कार्रवाई की आवश्यकता होती है। यह execveसिस्टम कॉल के हिस्से के रूप में किया जाता है । कर्नेल को फ़ाइल अनुमतियों को सत्यापित करने की आवश्यकता है, वर्तमान में कॉल करने की प्रक्रिया में चल रही निष्पादन योग्य फ़ाइल से मुक्त संसाधन (मेमोरी, आदि), नई निष्पादन योग्य फ़ाइल के लिए संसाधन आवंटित करें, और नए प्रोग्राम (और अधिक चीजें) के लिए नियंत्रण स्थानांतरित करें। मैं उल्लेख नहीं करूंगा)। execveसिस्टम कॉल वर्तमान में चल प्रक्रिया के कोड की जगह; forkएक नई प्रक्रिया बनाने के लिए एक अलग सिस्टम कॉल है।

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

शेबबैंग तंत्र कर्नेल को दूसरे प्रोग्राम में कोड की व्याख्या करने के कार्य को स्थगित करने की अनुमति देता है। जब कर्नेल देखता है कि निष्पादन योग्य फ़ाइल के साथ शुरू होता है #!, तो यह अगले कुछ वर्णों को पढ़ता है और फ़ाइल की पहली पंक्ति की व्याख्या करता है ( #!एक और फ़ाइल के लिए पथ के रूप में अग्रणी और वैकल्पिक स्थान) (प्लस तर्क, जिसके बारे में मैं यहां चर्चा नहीं करूंगा )। जब कर्नेल को फ़ाइल निष्पादित करने के लिए कहा जाता है /my/script, और यह देखता है कि फ़ाइल लाइन से शुरू होती है #!/some/interpreter, तो कर्नेल /some/interpreterतर्क के साथ निष्पादित होता है /my/script। फिर यह /some/interpreterतय करना है कि /my/scriptयह एक स्क्रिप्ट फ़ाइल है जिसे इसे निष्पादित करना चाहिए।

क्या होगा यदि किसी फ़ाइल में न तो मूल कोड होता है जो उस प्रारूप में होता है जिसे कर्नेल समझता है, और एक शेलबैंग से शुरू नहीं होता है? ठीक है, तो फ़ाइल निष्पादन योग्य नहीं है, और execveसिस्टम कोड त्रुटि कोड ENOEXEC(निष्पादन योग्य प्रारूप त्रुटि) के साथ विफल हो जाता है ।

यह कहानी का अंत हो सकता है, लेकिन अधिकांश गोले एक कमबैक सुविधा को लागू करते हैं। यदि कर्नेल लौटता है ENOEXEC, तो शेल फ़ाइल की सामग्री को देखता है और जाँचता है कि क्या यह शेल स्क्रिप्ट की तरह दिखता है। यदि शेल को लगता है कि फ़ाइल शेल स्क्रिप्ट की तरह दिखती है, तो वह इसे स्वयं निष्पादित करता है। यह कैसे करता है इसका विवरण शेल पर निर्भर करता है। आप ps $$अपनी स्क्रिप्ट में जोड़कर क्या हो रहा है, कुछ और देख सकते हैं और इस प्रक्रिया को देखने के साथ strace -p1234 -f -eprocessजहां 1234 शेल की पीआईडी ​​है।

बैश में, कॉलबैक तंत्र को कॉल करके लागू किया जाता है forkलेकिन नहीं execve। चाइल्ड बैश प्रक्रिया अपनी आंतरिक स्थिति को अपने आप साफ कर देती है और इसे चलाने के लिए नई स्क्रिप्ट फ़ाइल को खोलती है। इसलिए स्क्रिप्ट चलाने वाली प्रक्रिया अभी भी मूल बैश कोड छवि का उपयोग कर रही है और मूल कमांड लाइन तर्क पास किए गए जब आपने मूल रूप से बैश का आह्वान किया था। ATT ksh उसी तरह से व्यवहार करता है।

% bash --norc
bash-4.3$ ./foo.sh 
  PID TTY      STAT   TIME COMMAND
21913 pts/2    S+     0:00 bash --norc

इसके विपरीत, डैश एक तर्क के रूप में पारित स्क्रिप्ट के पथ के साथ ENOEXECकॉल करके प्रतिक्रिया करता है /bin/sh। दूसरे शब्दों में, जब आप डैश से एक शेल्गबुल स्क्रिप्ट का निष्पादन करते हैं, तो यह ऐसा व्यवहार करता है जैसे कि स्क्रिप्ट में शेलबैंग लाइन होती है #!/bin/sh। Mksh और zsh एक ही तरह से व्यवहार करते हैं।

% dash
$ ./foo.sh
  PID TTY      STAT   TIME COMMAND
21427 pts/2    S+     0:00 /bin/sh ./foo.sh

महान, जवाब को समझना। एक सवाल आरई: आपके द्वारा समझाया गया फॉलबैक कार्यान्वयन: मुझे लगता है कि एक बच्चे bashको कांटा जाता है, तो argv[]उसके अभिभावक के पास उसी सरणी तक पहुंच होती है , जो यह बताता है कि "मूल कमांड लाइन तर्क पारित हो गए जब आपने मूल रूप से बैश का आह्वान किया था", और यदि तो, यही कारण है कि बच्चे को एक स्पष्ट तर्क के रूप में मूल स्क्रिप्ट पारित नहीं किया जाता है (इसलिए क्यों यह grep द्वारा खोजने योग्य नहीं है) - क्या यह सही है?
स्टोनथ्रो

1
आप वास्तव में कर्नेल शेबंग व्यवहार को बंद कर सकते हैं ( BINFMT_SCRIPTमॉड्यूल इसे नियंत्रित करता है और इसे हटाया जा सकता है / संशोधित किया जा सकता है, हालांकि यह आमतौर पर सांख्यिकीय रूप से कर्नेल में जुड़ा होता है), लेकिन मुझे नहीं लगता कि आप कभी क्यों चाहते हैं, सिवाय एक एम्बेडेड सिस्टम में। । इस संभावना के लिए वर्कअराउंड के रूप में, क्षतिपूर्ति करने के लिए bashएक कॉन्फ़िगरेशन ध्वज ( HAVE_HASH_BANG_EXEC) है!
एरिकएफ

2
@StoneThrow यह इतना अधिक नहीं है कि बच्चा "मूल कमांड लाइन तर्क जानता है", क्योंकि यह उन्हें संशोधित नहीं करता है। एक प्रोग्राम psकमांड लाइन के तर्कों के रूप में क्या रिपोर्ट को संशोधित कर सकता है, लेकिन केवल एक बिंदु तक: इसे मौजूदा मेमोरी बफर को संशोधित करना है, यह इस बफर को बड़ा नहीं कर सकता है। इसलिए अगर बैश ने argvस्क्रिप्ट के नाम को जोड़ने के लिए इसे संशोधित करने की कोशिश की , तो यह हमेशा काम नहीं करेगा। बच्चा "एक तर्क पारित" नहीं करता है क्योंकि execveबच्चे में कभी भी सिस्टम कॉल नहीं होता है । यह सिर्फ वही बैश प्रोसेस इमेज है जो चलती रहती है।
गिल्स एसओ- बुराई को रोकें '

-1

पहले मामले में, स्क्रिप्ट आपके वर्तमान शेल से एक कांटे से बच्चे द्वारा चलाया जाता है।

आपको पहले दौड़ना चाहिए echo $$और फिर एक शेल पर एक नज़र डालनी चाहिए, जिसमें आपके शेल का प्रोसेस आईडी पैरेंट प्रोसेस आईडी है।

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