क्या $ 0 हमेशा स्क्रिप्ट में पथ शामिल करेगा?


11

मैं वर्तमान स्क्रिप्ट को पकड़ना चाहता हूं ताकि मैं शीर्ष पर टिप्पणी अनुभाग से मदद और संस्करण जानकारी प्रिंट कर सकूं।

मैं कुछ इस तरह से सोच रहा था:

grep '^#h ' -- "$0" | sed -e 's/#h //'

लेकिन फिर मैंने सोचा कि यदि स्क्रिप्ट एक निर्देशिका में स्थित थी जो पीएटीएच में थी और निर्देशिका को स्पष्ट रूप से निर्दिष्ट किए बिना बुलाया गया था।

मैंने विशेष चर के स्पष्टीकरण के लिए खोज की और निम्नलिखित विवरण प्राप्त किए $0:

  • वर्तमान शेल या प्रोग्राम का नाम

  • वर्तमान स्क्रिप्ट का नाम

  • स्क्रिप्ट का नाम

  • इसे चलाया गया था

इनमें से कोई भी यह स्पष्ट नहीं करता है $0कि स्क्रिप्ट के बिना इसे लागू किया गया था या नहीं, मान का मान निर्देशिका में शामिल होगा या नहीं । पिछले एक वास्तव में मुझे लगता है कि यह नहीं होगा।

मेरे सिस्टम पर परीक्षण (बैश 4.1)

मैंने / usr / स्थानीय / बिन में एक निष्पादन योग्य फ़ाइल बनाई जिसे एक लाइन के साथ स्क्रिप्टनाम कहा जाता है echo $0और इसे विभिन्न स्थानों से मंगवाया गया है ।

ये मेरे परिणाम हैं:

> cd /usr/local/bin/test
> ../scriptname
../scriptname

> cd /usr/local/bin
> ./scriptname
./scriptname

> cd /usr/local
> bin/scriptname
bin/scriptname

> cd /tmp
> /usr/local/bin/scriptname
/usr/local/bin/scriptname

> scriptname
/usr/local/bin/scriptname

इन परीक्षणों में, $0हमेशा मान होता है कि स्क्रिप्ट को कैसे लागू किया गया था, भले ही वह बिना किसी पथ घटक के लागू किया गया हो। उस मामले में, का मान $0है निरपेक्ष पथ । ताकि ऐसा लगे कि किसी अन्य कमांड को पास करना सुरक्षित होगा।

लेकिन तब मुझे स्टैक ओवरफ्लो पर एक टिप्पणी आई जिसने मुझे भ्रमित कर दिया। $(dirname $0)वर्तमान स्क्रिप्ट की निर्देशिका प्राप्त करने के लिए उत्तर का उपयोग करने का सुझाव देता है । टिप्पणी (7 बार अपवित्र) कहती है कि "यदि स्क्रिप्ट आपके मार्ग में है तो काम नहीं करेगा"।

प्रशन

  • क्या वह टिप्पणी सही है?
  • क्या व्यवहार अन्य प्रणालियों पर अलग है?
  • क्या ऐसी परिस्थितियां हैं जहां $0निर्देशिका शामिल नहीं होगी?

लोगों ने उन स्थितियों के बारे में उत्तर दिया है जहां $0स्क्रिप्ट के अलावा कुछ और है, जो प्रश्न शीर्षक का जवाब देता है। हालांकि, मैं उन स्थितियों में भी दिलचस्पी रखता हूं जहां $0स्क्रिप्ट ही है, लेकिन निर्देशिका शामिल नहीं है। विशेष रूप से, मैं SO उत्तर पर की गई टिप्पणी को समझने की कोशिश कर रहा हूं।
विषयांतर

जवाबों:


17

सबसे सामान्य मामलों में, $0स्क्रिप्ट के लिए एक पथ, निरपेक्ष या सापेक्ष होगा, इसलिए

script_path=$(readlink -e -- "$0")

(यह मानते हुए कि एक readlinkकमांड है और यह समर्थन करता है -e) आम तौर पर स्क्रिप्ट के लिए कैनोनिकल निरपेक्ष पथ प्राप्त करने का एक अच्छा तरीका है।

$0 इंटरप्रेटर में पास किए गए स्क्रिप्ट को निर्दिष्ट करने वाले तर्क से असाइन किया गया है।

उदाहरण के लिए:

the-shell -shell-options the/script its args

$0हो जाता है the/script

जब आप दौड़ते हैं:

the/script its args

आपका खोल एक करेगा:

exec("the/script", ["the/script", "its", "args"])

यदि स्क्रिप्ट में #! /bin/sh -उदाहरण के लिए वह धमाकेदार है, तो सिस्टम उसे बदल देगा:

exec("/bin/sh", ["/bin/sh" or "the/script", "-", "the/script", "its", "args"])

(यदि इसमें वह-बैंग शामिल नहीं है, या अधिक आम तौर पर यदि सिस्टम एक ENOEXEC त्रुटि देता है, तो आपका शेल जो कि एक ही काम करेगा)

कुछ सिस्टम पर सेतु / सेटगिड स्क्रिप्ट के लिए एक अपवाद है, जहां सिस्टम स्क्रिप्ट को कुछ पर खोल देगा fd xऔर इसके बजाय चलाएगा:

exec("/bin/sh", ["/bin/sh" or "the/script", "-", "/dev/fd/x", "its", "args"])

दौड़ की स्थिति से बचने के लिए (जिसमें मामला $0शामिल होगा /dev/fd/x)।

अब, आप यह तर्क दे सकते हैं कि /dev/fd/x यह स्क्रिप्ट का एक मार्ग है। ध्यान दें कि यदि आप से पढ़ते हैं $0, तो आप इनपुट का उपभोग करते हुए स्क्रिप्ट को तोड़ देंगे।

अब, अगर स्क्रिप्ट कमांड नाम को आमंत्रित किया जाता है तो इसमें कोई अंतर नहीं होता है। में:

the-script its args

आपका खोल ऊपर दिखेगा the-scriptमें $PATH$PATHकुछ निर्देशिकाओं के लिए निरपेक्ष या सापेक्ष (रिक्त स्ट्रिंग सहित) पथ हो सकते हैं। उदाहरण के लिए, यदि वर्तमान निर्देशिका में $PATHशामिल है /bin:/usr/bin:और the-scriptपाया जाता है, तो शेल एक करेगा:

exec("the-script", ["the-script", "its", "args"])

जो बन जाएगा:

exec("/bin/sh", ["/bin/sh" or "the-script", "-", "the-script", "its", "args"]

या यदि यह पाया जाता है /usr/bin:

exec("/usr/bin/the-script", ["the-script", "its", "args"])
exec("/bin/sh", ["/bin/sh" or "the-script" or "/usr/bin/the-script",
     "-", "/usr/bin/the-script", "its", "args")

उपर्युक्त कोने के मामले को छोड़कर उन सभी मामलों में, $0स्क्रिप्ट में एक पथ (पूर्ण या सापेक्ष) होगा।

अब, एक स्क्रिप्ट के रूप में भी कहा जा सकता है:

the-interpreter the-script its args

जब the-scriptऊपर से स्लैश वर्ण नहीं होते हैं, तो व्यवहार शेल से शेल में थोड़ा भिन्न होता है।

पुराने एटी एंड टी kshकार्यान्वयन वास्तव में स्क्रिप्ट को बिना शर्त के देख रहे थे $PATH(जो वास्तव में बग और सेतु लिपियों के लिए एक सुरक्षा छेद $0था ), इसलिए वास्तव में स्क्रिप्ट में एक पथ शामिल नहीं था जब तक कि $PATHवास्तव the-scriptमें वर्तमान निर्देशिका में खोजने के लिए लुकअप नहीं हुआ था ।

यदि यह पठनीय है, तो नया AT & T वर्तमान निर्देशिका में kshप्रयास और व्याख्या करेगा the-script। यदि नहीं यह एक पठनीय के लिए देखने हैं और निष्पादन the-script में $PATH

इसके लिए bash, यह जाँच करता है कि the-scriptक्या वर्तमान निर्देशिका में है (और एक टूटी हुई सिमलिंक नहीं है) और यदि नहीं, तो एक पठनीय (आवश्यक रूप से निष्पादन योग्य नहीं) the-scriptके लिए खोज $PATH

zshमें shअनुकरण की तरह करना होगा bash, सिवाय इसके कि यदि the-scriptवर्तमान निर्देशिका में एक टूट सिमलिंक है, यह एक के लिए खोज नहीं होगा the-scriptमें $PATHहै और इसके बजाय एक त्रुटि रिपोर्ट करेंगे।

अन्य सभी बॉर्न-जैसे गोले the-scriptऊपर नहीं दिखते $PATH

वैसे भी उन सभी गोले के लिए, यदि आप पाते हैं कि $0इसमें शामिल नहीं है /और पठनीय नहीं है, तो संभवतः इसे ऊपर देखा गया है $PATH। फिर, जैसा कि फ़ाइलों के $PATHनिष्पादन योग्य होने की संभावना है, यह संभवतः command -v -- "$0"अपने पथ को खोजने के लिए उपयोग करने के लिए एक सुरक्षित सन्निकटन है (हालांकि यह काम नहीं करेगा यदि $0शेल शेल या कीवर्ड (अधिकांश गोले में) का नाम भी हो।

इसलिए यदि आप वास्तव में उस मामले के लिए कवर करना चाहते हैं, तो आप इसे लिख सकते हैं:

progname=$0
[ -r "$progname" ] || progname=$(
    IFS=:; set -f
    for i in ${PATH-$(getconf PATH)}""; do
      case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
      esac
      [ -r "$p" ] && exec printf '%s\n' "$p"
    done
    exit 1
  ) && progname=$(readlink -e -- "$progname") ||
  progname=unknown

( ""संलग्न करने $PATHके लिए एक अनुगामी खाली तत्व को गोले के साथ संरक्षित करना है, जो विभाजक के बजाय सीमांकक के$IFS रूप में कार्य करता है )।

अब, एक स्क्रिप्ट को लागू करने के लिए अधिक गूढ़ तरीके हैं। एक कर सकता है:

the-shell < the-script

या:

cat the-script | the-shell

उस मामले में, $0पहला तर्क होगा ( argv[0]) कि इंटरप्रेटर प्राप्त हुआ (ऊपर the-shell, लेकिन वह कुछ भी हो सकता है, हालांकि आम तौर पर या तो आधार या उस दुभाषिया के लिए एक रास्ता)।

यह पता लगाना कि आप उस स्थिति में हैं, जिसके आधार पर मूल्य $0विश्वसनीय नहीं है। आप ps -o args= -p "$$"एक सुराग पाने के लिए आउटपुट को देख सकते हैं। पाइप के मामले में, कोई वास्तविक तरीका नहीं है जिससे आप स्क्रिप्ट पर वापस जा सकते हैं।

एक भी कर सकता है:

the-shell -c '. the-script' blah blih

फिर, zsh(और बॉर्न शेल के कुछ पुराने कार्यान्वयन को छोड़कर ), $0होगा blah। फिर, उन गोले में स्क्रिप्ट के रास्ते पर आना मुश्किल है।

या:

the-shell -c "$(cat the-script)" blah blih

आदि।

यह सुनिश्चित करने के लिए कि आपके पास अधिकार है $progname, आप इसमें एक विशिष्ट स्ट्रिंग की खोज कर सकते हैं:

progname=$0
[ -r "$progname" ] || progname=$(
    IFS=:; set -f
    for i in ${PATH-$(getconf PATH)}:; do
      case $i in
        "") p=$progname;;
        */) p=$i$progname;;
        *) p=$i/$progname
      esac
      [ -r "$p" ] && exec printf '%s\n' "$p"
    done
    exit 1
  ) && progname=$(readlink -e -- "$progname") ||
  progname=unknown

[ -f "$progname" ] && grep -q 7YQLVVD3UIUDTA32LSE8U9UOHH < "$progname" ||
  progname=unknown

लेकिन फिर से मुझे नहीं लगता कि यह प्रयास के लायक है।


स्टीफन, मैं "-"उपरोक्त उदाहरणों में आपके उपयोग को नहीं समझता । मेरे अनुभव में, एक दुभाषिया विकल्प की संभावना के साथ , exec("the-script", ["the-script", "its", "args"])बन जाता exec("/the/interpreter", ["/the/interpreter", "the-script", "its", "args"])है।
jrw32982

@ jrw32982, #! /bin/sh -है "हमेशा का उपयोग cmd -- somethingकरता है, तो आप गारंटी नहीं दे सकते कि somethingके साथ शुरू नहीं होगा -" अच्छा अभ्यास कहावत यहाँ के लिए आवेदन किया /bin/shहै (जहां -अंत के विकल्प मार्कर की तुलना में अधिक पोर्टेबल है के रूप में --) के साथ somethingकी राह / नाम किया जा रहा है स्क्रिप्ट। यदि आप सेतुकृत लिपियों के लिए इसका उपयोग नहीं करते हैं (उन प्रणालियों पर जो उनका समर्थन करते हैं, लेकिन उत्तर में उल्लिखित / dev / fd / x विधि के साथ नहीं), तो किसी को आपकी लिपि में -iया उसके -sलिए एक सिम्लिंक बनाकर रूट शेल मिल सकता है। उदाहरण।
स्टीफन चेजलस

धन्यवाद, स्टीफन। मैं अपने उदाहरण शेबंग लाइन में अनुगामी एकल हाइफ़न से चूक गया था। मुझे इस बात के लिए शिकार करना पड़ा कि यह कहाँ दर्ज़ है कि एक एकल हाइफ़न एक डबल हाइफ़न pubs.opengroup.org/onlinepubs/9699919799/utilities/sh.html के बराबर है ।
jrw32982

एक सेटिड स्क्रिप्ट के लिए शेबंग लाइन में अनुगामी एकल / डबल हाइफ़न को भूलना बहुत आसान है; किसी तरह सिस्टम को आपके लिए इसका ध्यान रखना चाहिए। सेतु लिपियों को पूरी तरह से छोड़ दें या किसी भी तरह से /bin/shअपनी स्वयं की प्रसंस्करण प्रक्रिया को निष्क्रिय कर दें यदि यह पता लगा सके कि यह चल रही है। मैं यह नहीं देखता कि कैसे / dev / fd / x का उपयोग करके खुद को ठीक करता है। मुझे अभी भी सिंगल / डबल हाइफ़न की आवश्यकता है, मुझे लगता है।
jrw32982

@ jrw32982, से /dev/fd/xशुरू होता है /, नहीं -। प्राथमिक लक्ष्य दोनों execve()एस के बीच दौड़ की स्थिति को दूर करना है (हालांकि, execve("the-script")जिसके बीच विशेषाधिकार बढ़ता है और बाद में execve("interpreter", "thescript")जहां interpreterथिसस्क्रिप्ट खुलता है बाद में (जो कि बहुत अच्छी तरह से इस बीच कुछ और के लिए एक सिम्लिंक के साथ बदल दिया गया है)। सुसाइड स्क्रिप्ट ठीक से नहीं execve("interpreter", "/dev/fd/n")बल्कि एक जगह है जहाँ n को पहले निष्पादित () के भाग के रूप में खोला गया है।
स्टीफन चेज़लस

6

यहां दो स्थितियां हैं जहां निर्देशिका शामिल नहीं होगी:

> bash scriptname
scriptname

> bash <scriptname
bash

दोनों मामलों में, वर्तमान निर्देशिका को वह निर्देशिका होना चाहिए जहां स्क्रिप्टनाम स्थित था।

पहले मामले में, यह $0अभी भी पारित किया जा सकता है grepक्योंकि यह मानता है कि File तर्क वर्तमान निर्देशिका के सापेक्ष है।

दूसरे मामले में, यदि सहायता और संस्करण की जानकारी केवल एक विशिष्ट कमांड लाइन विकल्प के जवाब में छपी है, तो यह एक समस्या नहीं होनी चाहिए। मुझे यकीन नहीं है कि कोई भी एक स्क्रिप्ट को कैसे मदद या संस्करण जानकारी मुद्रित करने के लिए आमंत्रित करेगा।

चेतावनियां

  • यदि स्क्रिप्ट वर्तमान निर्देशिका को बदल देती है, तो आप सापेक्ष पथ का उपयोग नहीं करना चाहेंगे।

  • यदि स्क्रिप्ट को सॉरी किया जाता है, तो $0आमतौर पर कॉलर स्क्रिप्ट के बजाय कॉल स्क्रिप्ट की वैल्यू होगी।


3

-cअधिकांश (सभी?) गोले के विकल्प का उपयोग करते समय एक मनमाना तर्क शून्य निर्दिष्ट किया जा सकता है । उदाहरण के लिए:

sh -c 'echo $0' argv0

से man bash(विशुद्ध रूप से चुना गया है क्योंकि यह मेरे से बेहतर विवरण है man sh- उपयोग समान है)

-सी

-C विकल्प मौजूद है, तो पहले गैर-विकल्प तर्क कमांड_स्ट्रिंग से कमांड पढ़े जाते हैं। यदि कमांड_स्ट्रिंग के बाद तर्क हैं, तो उन्हें $ 0 से शुरू करके, स्थितीय मापदंडों को सौंपा गया है।


मुझे लगता है कि यह काम करता है क्योंकि -c 'कमांड' ऑपरेंड है, और argv0इसका पहला नॉनपरैंड कमांड कमांड तर्क है।
mikeserv

@ ठीक है, मैंने एक आदमी स्निपेट के साथ अपडेट किया है।
ग्रीम

3

नोट: अन्य ने पहले से ही यांत्रिकी की व्याख्या की है $0इसलिए मैं वह सब छोड़ दूँगा।

मैं आम तौर पर इस पूरे मामले में कदम रखता हूं और बस कमांड का उपयोग करता हूं readlink -f $0। यह आपको हमेशा एक तर्क के रूप में आपको जो भी देता है, उसका पूरा रास्ता वापस देगा।

उदाहरण

कहो मैं यहाँ से शुरू करने के लिए हूँ:

$ pwd
/home/saml/tst/119929/adir

निर्देशिका + फ़ाइल बनाएँ:

$ mkdir adir
$ touch afile
$ cd adir/

अब दिखावा करना शुरू करें readlink:

$ readlink -f ../adir
/home/saml/tst/119929/adir

$ readlink -f ../
/home/saml/tst/119929

$ readlink -f ../afile 
/home/saml/tst/119929/afile

$ readlink -f .
/home/saml/tst/119929/adir

अतिरिक्त चालबाजी

अब एक सुसंगत परिणाम के साथ जब हम पूछताछ करते हैं $0तो readlinkहम dirname $(readlink -f $0)स्क्रिप्ट basename $(readlink -f $0)का वास्तविक नाम प्राप्त करने के लिए स्क्रिप्ट का पूर्ण पथ प्राप्त करने के लिए बस का उपयोग कर सकते हैं ।


0

मेरा manपेज कहता है:

$0: के लिए विस्तारित nameकी shellया shell script

ऐसा लगता है कि यह argv[0]वर्तमान शेल का अनुवाद है - या पहले नॉनपरैंड कमांड लाइन तर्क है कि वर्तमान में व्याख्या किए गए शेल को आमंत्रित किए जाने पर खिलाया जाता है। मैंने पहले कहा था कि sh ./somescriptइसके वैरिएबल को रास्ता दिया जाएगा, लेकिन यह गलत था क्योंकि यह अपनी खुद की एक नई प्रक्रिया है और एक नए के साथ इनवाइट किया गया है $0 $ENV shshell$ENV

इस तरह से sh ./somescript.shअलग है . ./somescript.shजो वर्तमान परिवेश में चलता है और $0पहले से ही सेट है।

आप इसकी तुलना करके $0देख सकते हैं /proc/$$/status

echo 'script="/proc/$$/status"
    echo $0
    cat "$script"' \
    > ./script.sh
sh ./script.sh ; . ./script.sh

सुधार के लिए धन्यवाद, @toxalot मैंने कुछ सीखा।


मेरे सिस्टम पर, अगर यह खट्टा ( . ./myscript.shया source ./myscript.sh) है, तो $0शेल है। लेकिन अगर इसे शेल के तर्क के रूप में पारित किया जाता है ( sh ./myscript.sh), तो $0स्क्रिप्ट का मार्ग है। बेशक, shमेरे सिस्टम पर बैश है। इसलिए मुझे नहीं पता कि इससे कोई फर्क पड़ता है या नहीं।
टॉक्सालॉट

क्या यह एक हैशबैंग है? मुझे लगता है कि अंतर मायने रखता है - और मैं इसे जोड़ सकता हूं - इसके बिना यह श नहीं होना चाहिए execऔर इसके बजाय स्रोत होना चाहिए , लेकिन इसके साथ, इसे करना चाहिए exec
मोकेसर

हैशबैंग के साथ या उसके बिना, मुझे वही परिणाम मिलते हैं। निष्पादन योग्य होने के साथ या इसके बिना, मुझे वही परिणाम मिलते हैं।
विषयांतर

मैं भी! मुझे लगता है कि यह है क्योंकि यह एक शेल बिलिन है। मैं जाँच कर रहा हूँ ...
mikeserv

बैंग लाइनों की व्याख्या कर्नेल द्वारा की जाती है। यदि आप ./somescriptबैंगलाइन के साथ निष्पादित करते हैं #!/bin/sh, तो यह चलने के बराबर होगा /bin/sh ./somescript। अन्यथा उन्हें खोल से कोई फर्क नहीं पड़ता।
ग्रीम

0

मैं वर्तमान स्क्रिप्ट को पकड़ना चाहता हूं ताकि मैं शीर्ष पर टिप्पणी अनुभाग से मदद और संस्करण जानकारी प्रिंट कर सकूं।

जहां $0स्क्रिप्ट नाम होता है, उसमें स्क्रिप्ट के रूप में उपसर्ग पथ शामिल हो सकता है जिस तरह से स्क्रिप्ट कहा जाता है, मैंने हमेशा ${0##*/}मदद आउटपुट में स्क्रिप्ट नाम को प्रिंट करने के लिए उपयोग किया है जो किसी भी अग्रणी पथ को हटा देता है $0

उन्नत बैश स्क्रिप्टिंग गाइड से लिया गया - धारा 10.2 पैरामीटर प्रतिस्थापन

${var#Pattern}

$varउस सबसे छोटे भाग से निकालें $Patternजिसके सामने का भाग मेल खाता है $var

${var##Pattern}

$varउस सबसे लंबे भाग से निकालें $Patternजिसके सामने का भाग मेल खाता है $var

तो $0उस मैच का सबसे लंबा हिस्सा */संपूर्ण पथ उपसर्ग होगा, केवल स्क्रिप्ट नाम लौटाएगा।


हां, मैं ऐसा उस स्क्रिप्ट के नाम के लिए भी करता हूं जिसका उपयोग मदद संदेश के भीतर किया जाता हैलेकिन मैं स्क्रिप्ट को ग्रिप करने के बारे में बात कर रहा हूं , इसलिए मुझे कम से कम एक सापेक्ष पथ की आवश्यकता है। मैं टिप्पणियों में शीर्ष पर मदद संदेश रखना चाहता हूं और बाद में इसे मुद्रित करते समय मदद संदेश दोहराना नहीं है।
टॉक्सालॉट

0

कुछ इसी तरह के लिए मैं उपयोग:

rPath="$(dirname $(realpath $0))"
echo $rPath 

rPath=$(dirname $(readlink -e -- "$0"))
echo $rPath 

rPath हमेशा एक ही मूल्य है।

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