कैसे पता लगाया जाए कि किसी स्क्रिप्ट में खटास आ रही है


217

मेरे पास एक स्क्रिप्ट है जहां मैं नहीं चाहता कि exitअगर यह खट्टा हो रहा है तो कॉल करें ।

मैंने जाँच की कि क्या $0 == bashयह समस्या है, लेकिन यदि स्क्रिप्ट किसी अन्य स्क्रिप्ट से सॉर्ट की जाती है, या यदि उपयोगकर्ता इसे किसी भिन्न शेल से स्रोत के रूप में देखता है ksh

क्या यह पता लगाने का एक विश्वसनीय तरीका है कि क्या किसी स्क्रिप्ट को सोर्स किया जा रहा है?


2
मेरे पास कुछ समय पहले एक समान मुद्दा था और सभी मामलों में 'निकास' से बचकर इसे हल किया; "किल-ऑफ $ $" स्क्रिप्ट को किसी भी स्थिति में सुरक्षित रूप से समाप्त कर देता है।
JESii

1
क्या आपने इस जवाब पर ध्यान दिया ? यह स्वीकृत से 5 साल बाद दिया जाता है, लेकिन इसमें "बैटरी शामिल है"।
रतिरु

जवाबों:


73

यह बैश और कोर्न के बीच पोर्टेबल लगता है:

[[ $_ != $0 ]] && echo "Script is being sourced" || echo "Script is a subshell"

इसके समान एक असाइनमेंट या pathname="$_"बाद के टेस्ट (एक्शन और एक्शन के साथ) स्क्रिप्ट की पहली लाइन या शेबबैंग के बाद की लाइन पर होना चाहिए (जो कि अगर इस्तेमाल किया जाता है, तो इसके लिए काम करने के लिए ksh होना चाहिए सबसे अधिक परिस्थितियाँ)।


10
दुर्भाग्य से यह काम करने की गारंटी नहीं है। यदि उपयोगकर्ता ने सेट किया है BASH_ENV, $_तो स्क्रिप्ट के शीर्ष पर से अंतिम कमांड रन होगा BASH_ENV
मिकेल

30
यह भी काम नहीं करेगा यदि आप स्क्रिप्ट को निष्पादित करने के लिए bash का उपयोग करते हैं, जैसे $ bash script.sh तो $ _ इसके बजाय / बिन / बैश होगा। स्क्रिप्ट, जो कि आप की अपेक्षा है, जब स्क्रिप्ट लागू होती है। इस तरह से: $ ./script.sh किसी भी मामले में पता लगाना $_एक समस्या है।
वाइरवान पुरवंतो

2
उन आह्वान विधियों की जांच के लिए अतिरिक्त परीक्षण शामिल किए जा सकते हैं।
अगली सूचना तक रोक दिया गया।

8
दुर्भाग्य से, यह गलत है! मेरा जवाब
F. Hauri

8
संक्षेप में: जबकि यह दृष्टिकोण आम तौर पर काम करता है, यह मजबूत नहीं है ; यह निम्नलिखित 2 परिदृश्यों में विफल रहता है: (क) bash script(जो इस समाधान misreports के रूप में खोल निष्पादन के माध्यम से मंगलाचरण, sourced ), और (ख) (अब तक कम होने की संभावना) echo bash; . script(यदि $_स्क्रिप्ट सोर्सिंग खोल मिलान करने के लिए होता है, इस समाधान misreports यह के रूप में एक उपखंड )। केवल शेल-विशिष्ट विशेष चर (जैसे, $BASH_SOURCE) मजबूत समाधानों की अनुमति देते हैं (यह इस प्रकार है कि कोई मजबूत POSIX- अनुरूप समाधान नहीं है)। यह है एक मजबूत पार खोल परीक्षण शिल्प के लिए संभव है, बोझिल, यद्यपि।
mklement0

170

यदि आपका बैश संस्करण BASH_SOURCE सरणी चर के बारे में जानता है, तो कुछ इस तरह आज़माएँ:

# man bash | less -p BASH_SOURCE
#[[ ${BASH_VERSINFO[0]} -le 2 ]] && echo 'No BASH_SOURCE array variable' && exit 1

[[ "${BASH_SOURCE[0]}" != "${0}" ]] && echo "script ${BASH_SOURCE[0]} is being sourced ..."

11
हो सकता है कि $ BASH_SOURCE के रूप में सबसे साफ तरीका उस उद्देश्य के लिए हो।
con-f-use

4
ध्यान दें कि यह ksh के तहत काम नहीं करेगा जो एक शर्त है जिसे ओपी निर्दिष्ट करता है।
अगली सूचना तक रोक दिया गया।

2
क्या ${BASH_SOURCE[0]}सिर्फ एक के बजाय उपयोग करने का कोई कारण है $BASH_SOURCE? और ${0}बनाम $0?
हरबन

4
BASH_SOURCEएक सरणी चर ( मैनुअल देखें ) जो स्रोतों का एक स्टैक ट्रेस रखता है, जहां ${BASH_SOURCE[0]}नवीनतम है। ब्रेसेस का उपयोग यहां बैश को बताने के लिए किया जाता है कि चर नाम का हिस्सा क्या है। वे $0इस मामले में आवश्यक नहीं हैं , लेकिन वे चोट भी नहीं करते हैं। ;)
कोनराड

4
@Konrad, और यदि आप विस्तार करते हैं $array, तो आप ${array[0]}डिफ़ॉल्ट रूप से प्राप्त करते हैं । तो, फिर, वहाँ एक कारण है [...]?
चार्ल्स डफी

133

के लिए मजबूत समाधान bash, ksh,zsh , एक सहित पार खोल एक के अलावा एक यथोचित मजबूत POSIX अनुरूप समाधान :

  • दिए गए वर्जन नंबर वे हैं जिन पर कार्यक्षमता को सत्यापित किया गया था - संभावना है, ये समाधान बहुत पहले के संस्करणों पर भी काम करते हैं, प्रतिक्रिया का स्वागत है

  • POSIX सुविधाओं का उपयोग केवल (जैसे कि dash, जो /bin/shउबंटू के रूप में कार्य करता है ), यह निर्धारित करने के लिए कोई मजबूत तरीका नहीं है कि क्या किसी स्क्रिप्ट को सॉर्ट किया जा रहा है - सर्वश्रेष्ठ सन्निकटन के लिए नीचे देखें ।

वन-लाइनर्स का पालन करें - नीचे स्पष्टीकरण; क्रॉस-शेल संस्करण जटिल है, लेकिन इसे मजबूती से काम करना चाहिए:

  • बैश (3.57 और 4.4.19 पर सत्यापित)

    (return 0 2>/dev/null) && sourced=1 || sourced=0
  • ksh (93u + पर सत्यापित)

    [[ $(cd "$(dirname -- "$0")" && 
       printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] &&
         sourced=1 || sourced=0
    
  • zsh (5.0.5 पर सत्यापित) - किसी फ़ंक्शन के बाहर इसे कॉल करना सुनिश्चित करें

    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0
  • क्रॉस-शेल (बैश, ksh, zsh)

    ([[ -n $ZSH_EVAL_CONTEXT && $ZSH_EVAL_CONTEXT =~ :file$ ]] || 
     [[ -n $KSH_VERSION && $(cd "$(dirname -- "$0")" &&
        printf '%s' "${PWD%/}/")$(basename -- "$0") != "${.sh.file}" ]] || 
     [[ -n $BASH_VERSION ]] && (return 0 2>/dev/null)) && sourced=1 || sourced=0
    
  • POSIX- शिकायत ; नहीं एक एक लाइनर (एकल पाइप लाइन) तकनीकी कारणों से और के लिए नहीं पूरी तरह से मजबूत (नीचे देखें):

    sourced=0
    if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
      case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
    elif [ -n "$KSH_VERSION" ]; then
      [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
    elif [ -n "$BASH_VERSION" ]; then
      (return 0 2>/dev/null) && sourced=1 
    else # All other shells: examine $0 for known shell binary filenames
      # Detects `sh` and `dash`; add additional shell filenames as needed.
      case ${0##*/} in sh|dash) sourced=1;; esac
    fi
    

स्पष्टीकरण:


दे घुमा के

(return 0 2>/dev/null) && sourced=1 || sourced=0

नोट: तकनीक को उपयोगकर्ता 5754163 के उत्तर से अनुकूलित किया गया था , क्योंकि यह मूल समाधान की तुलना में अधिक मजबूत निकला, [[ $0 != "$BASH_SOURCE" ]] && sourced=1 || sourced=0[1]

  • बैश returnकेवल फ़ंक्शंस से स्टेटमेंट्स की अनुमति देता है और, स्क्रिप्ट के टॉप-लेवल स्कोप में, केवल तभी स्क्रिप्ट को सॉर्ट किया जाता है

    • यदि गैर-स्रोत वाली स्क्रिप्ट returnके शीर्ष-स्तरीय दायरे में उपयोग किया जाता है , तो एक त्रुटि संदेश उत्सर्जित होता है, और बाहर निकलने का कोड निर्धारित होता है ।1
  • (return 0 2>/dev/null)returnएक उपधारा में निष्पादित होता है और त्रुटि संदेश को दबा देता है; बाद में निकास कोड इंगित करता है कि क्या स्क्रिप्ट को सॉर्ट किया गया था ( 0या नहीं 1) ( ), जिसका उपयोग &&और ||ऑपरेटर को sourcedतदनुसार चर सेट करने के लिए किया जाता है।

    • एक उपखंड का उपयोग करना आवश्यक है, क्योंकि returnएक खटारा स्क्रिप्ट के शीर्ष-स्तरीय दायरे में निष्पादित करने से स्क्रिप्ट बाहर निकल जाएगी।
    • @Haozhun को टोपी की टिप , जिन्होंने कमांड के 0रूप में स्पष्ट रूप से उपयोग करके कमांड को अधिक मजबूत बनाया return; वह नोट करता है: प्रति बैश मदद return [N]: "यदि एन छोड़ा गया है, तो वापसी की स्थिति अंतिम कमांड की है।" परिणामस्वरूप, पहले वाला संस्करण [जो returnबिना किसी ऑपरेंड के उपयोग किया गया था ] गलत परिणाम उत्पन्न करता है यदि उपयोगकर्ता के शेल पर अंतिम कमांड में गैर-शून्य रिटर्न वैल्यू है।

क्ष

[[ \
   $(cd "$(dirname -- "$0")" && printf '%s' "${PWD%/}/")$(basename -- "$0") != \
   "${.sh.file}" \
]] && 
sourced=1 || sourced=0

विशेष चर ${.sh.file}कुछ हद तक अनुरूप है $BASH_SOURCE; ध्यान दें कि बैश, zsh और डैश में ${.sh.file}एक सिंटैक्स त्रुटि होती है, इसलिए इसे बहु-शेल स्क्रिप्ट में सशर्त रूप से निष्पादित करना सुनिश्चित करें ।

बैश के विपरीत, $0और गैर-खट्टे मामले में बिल्कुल समान ${.sh.file}होने की गारंटी नहीं है , जैसा कि एक सापेक्ष पथ हो सकता है , जबकि हमेशा एक पूर्ण पथ होता है, इसलिए तुलना करने से पहले एक पूर्ण पथ पर हल होना चाहिए।$0${.sh.file}$0


zsh

[[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && sourced=1 || sourced=0

$ZSH_EVAL_CONTEXTमूल्यांकन के संदर्भ के बारे में जानकारी शामिल है - इसे किसी फ़ंक्शन के बाहर कॉल करें। एक खट्टा स्क्रिप्ट के अंदर [शीर्ष स्तर गुंजाइश], के साथ $ZSH_EVAL_CONTEXT समाप्त होता है :file

कैविएट: एक कमांड प्रतिस्थापन के अंदर, zsh एपेंड करता है :cmdsubst, इसलिए वहां के $ZSH_EVAL_CONTEXTलिए परीक्षण करें :file:cmdsubst$


POSIX सुविधाओं का उपयोग केवल

यदि आप कुछ मान्यताओं को बनाने के लिए तैयार हैं, तो आप एक उचित, लेकिन मूर्खतापूर्ण अनुमान नहीं लगा सकते हैं कि क्या आपकी स्क्रिप्ट को खट्टा किया जा रहा है, जो कि आपके स्क्रिप्ट को निष्पादित करने वाले गोले के बाइनरी फाइलनाम को जानने के आधार पर किया जा सकता है
विशेष रूप से, इसका मतलब है कि यदि आपकी स्क्रिप्ट किसी अन्य स्क्रिप्ट द्वारा सॉर्ट की जा रही है तो यह दृष्टिकोण विफल हो जाता है ।

खंड "खट्टा इनवोकेशन को कैसे संभालें" मेरा इस जवाब में उन किनारे के मामलों पर चर्चा की गई है जो केवल पोसिक्स सुविधाओं के साथ विस्तार से नहीं किए जा सकते हैं

यह मानक व्यवहार पर निर्भर करता है $0, जो zsh, उदाहरण के लिए प्रदर्शित नहीं करता है।

इस प्रकार, सबसे सुरक्षित दृष्टिकोण सभी शेष गोले के लिए एक ठोस समाधान के साथ मजबूत, शेल-विशिष्ट तरीकों को संयोजित करना है

स्टीफन डेसनेक्स के लिए टोपी की टिप और प्रेरणा के लिए उनका जवाब (मेरे क्रॉस-शेल स्टेटमेंट एक्सप्रेशन को एक shअसंगत ifकथन में बदलना और अन्य शेल के लिए हैंडलर जोड़ना)।

sourced=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
  case $ZSH_EVAL_CONTEXT in *:file) sourced=1;; esac
elif [ -n "$KSH_VERSION" ]; then
  [ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ] && sourced=1
elif [ -n "$BASH_VERSION" ]; then
  (return 0 2>/dev/null) && sourced=1 
else # All other shells: examine $0 for known shell binary filenames
  # Detects `sh` and `dash`; add additional shell filenames as needed.
  case ${0##*/} in sh|dash) sourced=1;; esac
fi

[1] user1902689 कि खोज की [[ $0 != "$BASH_SOURCE" ]]पैदावार एक झूठी सकारात्मक है जब आप एक स्क्रिप्ट को निष्पादित में स्थित$PATH अपने पास करके मात्र फ़ाइल नाम को bashबाइनरी; जैसे, bash my-scriptक्योंकि $0बस है तो my-script, जबकि $BASH_SOURCEहै पूरा पथ । आप सामान्य रूप से में स्क्रिप्ट आह्वान करने के लिए इस तकनीक का प्रयोग नहीं करेंगे $PATH- आप सिर्फ उन्हें आह्वान था सीधे ( my-script) - यह है जब साथ संयुक्त उपयोगी -xके लिए डिबगिंग


1
इस तरह के एक व्यापक जवाब के लिए यश।
शाम

75

@ डेनिसविलियम्सन के उत्तर को पढ़ने के बाद, कुछ मुद्दे हैं, नीचे देखें:

जैसा कि यह सवाल खड़ा है तथा , इस उत्तर में एक और हिस्सा है ... निचे देखो।

सरल मार्ग

[ "$0" = "$BASH_SOURCE" ]

आइए कोशिश करें (मक्खी पर क्योंकि वह मार सकता है; ;-):

source <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

bash <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 16229 is own (/dev/fd/63, /dev/fd/63)

मैं पठनीयता के sourceबदले उपयोग करता हूं .(जैसा .कि एक उपनाम है source):

. <(echo $'#!/bin/bash
           [ "$0" = "$BASH_SOURCE" ] && v=own || v=sourced;
           echo "process $$ is $v ($0, $BASH_SOURCE)" ')
process 29301 is sourced (bash, /dev/fd/63)

ध्यान दें कि प्रक्रिया संख्या में परिवर्तन नहीं होता है जबकि प्रक्रिया खट्टी रहती है :

echo $$
29301

$_ == $0तुलना का उपयोग क्यों नहीं करना है

कई मामले सुनिश्चित करने के लिए, मैं एक सच्ची स्क्रिप्ट लिखना शुरू करता हूं :

#!/bin/bash

# As $_ could be used only once, uncomment one of two following lines

#printf '_="%s", 0="%s" and BASH_SOURCE="%s"\n' "$_" "$0" "$BASH_SOURCE"
[[ "$_" != "$0" ]] && DW_PURPOSE=sourced || DW_PURPOSE=subshell

[ "$0" = "$BASH_SOURCE" ] && BASH_KIND_ENV=own || BASH_KIND_ENV=sourced;
echo "proc: $$[ppid:$PPID] is $BASH_KIND_ENV (DW purpose: $DW_PURPOSE)"

इसे एक फ़ाइल में कॉपी करें testscript:

cat >testscript   
chmod +x testscript

अब हम परीक्षण कर सकते हैं:

./testscript 
proc: 25758[ppid:24890] is own (DW purpose: subshell)

ठीक है।

. ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

source ./testscript 
proc: 24890[ppid:24885] is sourced (DW purpose: sourced)

ठीक है।

लेकिन, -xझंडा जोड़ने से पहले एक स्क्रिप्ट का परीक्षण करने के लिए :

bash ./testscript 
proc: 25776[ppid:24890] is own (DW purpose: sourced)

या पूर्व-निर्धारित चर का उपयोग करने के लिए:

env PATH=/tmp/bintemp:$PATH ./testscript 
proc: 25948[ppid:24890] is own (DW purpose: sourced)

env SOMETHING=PREDEFINED ./testscript 
proc: 25972[ppid:24890] is own (DW purpose: sourced)

यह अब और काम नहीं करेगा।

5 वीं पंक्ति से 6 वीं तक चलती टिप्पणी अधिक पठनीय उत्तर देगी:

./testscript 
_="./testscript", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26256[ppid:24890] is own

. testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

source testscript 
_="_filedir", 0="bash" and BASH_SOURCE="testscript"
proc: 24890[ppid:24885] is sourced

bash testscript 
_="/bin/bash", 0="testscript" and BASH_SOURCE="testscript"
proc: 26317[ppid:24890] is own

env FILE=/dev/null ./testscript 
_="/usr/bin/env", 0="./testscript" and BASH_SOURCE="./testscript"
proc: 26336[ppid:24890] is own

और जोर से: अभी...

जैसा कि मैं उपयोग नहीं करता बहुत कुछ, मैन पेज पर कुछ पढ़ने के बाद, मेरी कोशिश है:

#!/bin/ksh

set >/tmp/ksh-$$.log

इसे इसमें कॉपी करें testfile.ksh:

cat >testfile.ksh
chmod +x testfile.ksh

इसे दो बार चलाएं:

./testfile.ksh
. ./testfile.ksh

ls -l /tmp/ksh-*.log
-rw-r--r-- 1 user user   2183 avr 11 13:48 /tmp/ksh-9725.log
-rw-r--r-- 1 user user   2140 avr 11 13:48 /tmp/ksh-9781.log

echo $$
9725

और देखो:

diff /tmp/ksh-{9725,9781}.log | grep ^\> # OWN SUBSHELL:
> HISTCMD=0
> PPID=9725
> RANDOM=1626
> SECONDS=0.001
>   lineno=0
> SHLVL=3

diff /tmp/ksh-{9725,9781}.log | grep ^\< # SOURCED:
< COLUMNS=152
< HISTCMD=117
< LINES=47
< PPID=9163
< PS1='$ '
< RANDOM=29667
< SECONDS=23.652
<   level=1
<   lineno=1
< SHLVL=2

एक खट्टा रन में कुछ चर है , लेकिन वास्तव में संबंधित कुछ भी नहीं ...

आप यह भी देख सकते हैं कि $SECONDSयह करीब है 0.000, लेकिन यह केवल मैनुअल खट्टा मामलों को सुनिश्चित करता है ...

तुम भी माता पिता क्या है के लिए जाँच करने की कोशिश कर सकते हैं :

इसे अपने में रखें testfile.ksh:

ps $PPID

से:

./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32320 pts/4    Ss     0:00 -ksh

. ./testfile.ksh
  PID TTY      STAT   TIME COMMAND
32319 ?        S      0:00 sshd: user@pts/4

या ps ho cmd $PPID, लेकिन यह काम केवल एक स्तर के सब्सक्रिप्शन के लिए ...

क्षमा करें, मुझे ऐसा करने का एक विश्वसनीय तरीका नहीं मिला


[ "$0" = "$BASH_SOURCE" ] || [ -z "$BASH_SOURCE" ]पाइप के माध्यम से पढ़ी जाने वाली लिपियों के लिए ( cat script | bash)।
हकर्रे

2
ध्यान दें कि इसके .लिए एक उपनाम नहीं है source, यह वास्तव में दूसरा तरीका है। source somescript.shBash-ism है और पोर्टेबल नहीं है, . somescript.shPOSIX और पोर्टेबल IIRC है।
ड्रैगन 88

32

BASH_SOURCE[]इस सवाल का जवाब (बैश-3.0 और बाद में) सबसे सरल लगता है, हालांकि BASH_SOURCE[]है एक समारोह शरीर के बाहर काम करने के लिए दस्तावेज नहीं (यह वर्तमान में आदमी पेज के साथ असहमति में, काम करने के लिए होता है)।

सबसे मजबूत तरीका है, जैसा कि Wirawan Purwanto द्वारा सुझाया गया है, FUNCNAME[1] एक फ़ंक्शन के भीतर जांचना है :

function mycheck() { declare -p FUNCNAME; }
mycheck

फिर:

$ bash sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="main")'
$ . sourcetest.sh
declare -a FUNCNAME='([0]="mycheck" [1]="source")'

यह आउटपुट के चेक caller, मान mainऔर sourceकॉलर के संदर्भ को अलग करने के बराबर है । उपयोग FUNCNAME[]आपको callerआउटपुट कैप्चर करने और पार्स करने से बचाता है । हालांकि आपको सही होने के लिए अपनी स्थानीय कॉल गहराई को जानना या गणना करना होगा। किसी स्क्रिप्ट को किसी अन्य फ़ंक्शन या स्क्रिप्ट के भीतर से अलग किए जाने वाले मामलों की वजह से सरणी (स्टैक) अधिक गहरी हो जाएगी। ( FUNCNAMEएक विशेष बैश एरे चर है, इसमें कॉल स्टैक के अनुरूप सन्निहित सूचकांक होना चाहिए, जब तक कि यह कभी न हो unset।)

function issourced() {
    [[ ${FUNCNAME[@]: -1} == "source" ]]
}

(बैश-4.2 में और बाद में आप ${FUNCNAME[-1]}सरणी में अंतिम आइटम के बजाय सरल रूप का उपयोग कर सकते हैं । नीचे डेनिस विलियमसन की टिप्पणी के लिए बेहतर और सरलीकृत धन्यवाद।)

हालाँकि, आपकी समस्या यह है कि " मेरे पास एक स्क्रिप्ट है जहां मैं नहीं चाहता कि इसे 'एग्जिट' कहा जाए, अगर यह खट्टा हो रहा है "। bashइस स्थिति के लिए सामान्य मुहावरा है:

return 2>/dev/null || exit

यदि स्क्रिप्ट को खट्टा किया जा रहा है, तो returnखट्टी स्क्रिप्ट को समाप्त कर देगा और कॉलर को वापस कर देगा।

यदि स्क्रिप्ट निष्पादित हो रही है, तो returnएक त्रुटि (पुनर्निर्देशित) लौटाएगा, और exitस्क्रिप्ट को सामान्य रूप से समाप्त कर देगा। दोनों returnऔर exitएक निकास कोड, यदि आवश्यक हो ले सकते हैं।

अफसोस की बात है, यह ksh(मेरे पास कम से कम एटी एंड टी व्युत्पन्न संस्करण में नहीं है) में काम नहीं करता है , यह एक फ़ंक्शन या डॉट-सोर्स किए गए स्क्रिप्ट के बाहर मंगाने के returnबराबर है exit

अपडेट किया गया : आपको क्या कर सकते हैं समकालीन संस्करण में क्या की kshविशेष चर की जाँच करने के लिए है .sh.levelजो समारोह कॉल गहराई को तैयार है। एक आहिस्ता-आहिस्ता लिपि के लिए, यह शुरू में परेशान होगा, एक डॉट-सॉर्ट की गई लिपि के लिए इसे 1 पर सेट किया जाएगा।

function issourced {
    [[ ${.sh.level} -eq 2 ]]
}

issourced && echo this script is sourced

यह बैश संस्करण के रूप में काफी मजबूत नहीं है, आपको issourced()उस फ़ाइल में आह्वान करना चाहिए जिसे आप शीर्ष स्तर से या किसी ज्ञात फ़ंक्शन गहराई पर परीक्षण कर रहे हैं।

(आप जीथब पर इस कोड में रुचि भी ले सकते हैं जो एक kshअनुशासन फ़ंक्शन और बैश FUNCNAMEसरणी का अनुकरण करने के लिए कुछ डिबग ट्रैप ट्रिअरी का उपयोग करता है ।)

यहाँ विहित जवाब: http://mywiki.wooledge.org/BashFAQ/109 भी प्रदान करता है $-खोल राज्य का एक अन्य सूचक (हालांकि अपूर्ण) के रूप में।


टिप्पणियाँ:

  • "मुख्य" और "स्रोत" ( बिलिन को ओवरराइड करना ) नाम के बैश फ़ंक्शंस बनाना संभव है , ये नाम दिखाई दे सकते हैं FUNCNAME[]लेकिन जब तक उस सरणी में केवल अंतिम आइटम का परीक्षण किया जाता है तब तक कोई अस्पष्टता नहीं होती है।
  • मेरे पास इसके लिए अच्छा जवाब नहीं है pdksh। निकटतम चीज़ जो मैं पा सकता हूँ pdksh, केवल उसी पर लागू होती है , जहाँ स्क्रिप्ट की प्रत्येक सोर्सिंग एक नई फ़ाइल डिस्क्रिप्टर (मूल स्क्रिप्ट के लिए 10 से शुरू) खोलती है। लगभग निश्चित रूप से कुछ ऐसा नहीं है जिस पर आप भरोसा करना चाहते हैं ...

${FUNCNAME[(( ${#FUNCNAME[@]} - 1 ))]}स्टैक में अंतिम (नीचे) आइटम कैसे प्राप्त करें? फिर "मुख्य" (ओपी के लिए नकारात्मक) के खिलाफ परीक्षण मेरे लिए सबसे विश्वसनीय था।
एड्रियन गुंटर

अगर मेरे पास एक PROMPT_COMMANDसेट है, जो कि FUNCNAMEमेरे चलाने पर सरणी के अंतिम सूचकांक के रूप में दिखाई देता है source sourcetest.sh। चेक इन्वर्ट करना ( mainअंतिम इंडेक्स के रूप में देखना ) अधिक मजबूत लगता है is_main() { [[ ${FUNCNAME[@]: -1} == "main" ]]; }:।
dimo414 14

1
मानव-पृष्ठ बताता है, जो FUNCNAMEकेवल कार्यों में उपलब्ध है। मेरे परीक्षणों के अनुसार declare -p FUNCNAME, bashअलग तरह से व्यवहार करता है। v4.3 कार्यों के बाहर एक त्रुटि देता है, जबकि v4.4 देता है declare -a FUNCNAME। दोनों (!) वापसी mainके लिए ${FUNCNAME[0]}मुख्य लिपि में (अगर यह क्रियान्वित किया जाता है), जबकि $FUNCNAMEकुछ भी नहीं देता है। और: $BASH_SOURCEबाहर के कार्यों का उपयोग करते हुए "स्क्रिप्ट" में बहुत सारी स्क्रिप्ट हैं , मुझे संदेह है कि यह बदल सकता है या बदल जाएगा।
टिनो

24

संपादक का ध्यान दें: इस उत्तर का समाधान मजबूत तरीके से काम करता है, लेकिन यह bashअकेला है। इसे सुव्यवस्थित किया जा सकता है
(return 2>/dev/null)

टी एल; डॉ

किसी returnकथन को निष्पादित करने का प्रयास करें । यदि स्क्रिप्ट में खटास नहीं है, तो यह एक त्रुटि पैदा करेगा। आप उस त्रुटि को पकड़ सकते हैं और आवश्यकतानुसार आगे बढ़ सकते हैं।

इसे किसी फ़ाइल में रखें और कहें, test.sh:

#!/usr/bin/env sh

# Try to execute a `return` statement,
# but do it in a sub-shell and catch the results.
# If this script isn't sourced, that will raise an error.
$(return >/dev/null 2>&1)

# What exit code did that give?
if [ "$?" -eq "0" ]
then
    echo "This script is sourced."
else
    echo "This script is not sourced."
fi

इसे सीधे निष्पादित करें:

shell-prompt> sh test.sh
output: This script is not sourced.

इसे स्रोत:

shell-prompt> source test.sh
output: This script is sourced.

मेरे लिए, यह zsh और bash में काम करता है।

व्याख्या

returnयदि आप एक समारोह के बाहर या यदि स्क्रिप्ट sourced नहीं है यह निष्पादित करने के लिए कोशिश बयान एक त्रुटि बढ़ा देंगे। इसे शेल प्रॉम्प्ट से आज़माएं:

shell-prompt> return
output: ...can only `return` from a function or sourced script

आपको उस त्रुटि संदेश को देखने की आवश्यकता नहीं है, इसलिए आप आउटपुट को dev / null में पुनर्निर्देशित कर सकते हैं:

shell-prompt> return >/dev/null 2>&1

अब एक्जिट कोड चेक करें। 0 का अर्थ है ठीक है (कोई त्रुटि नहीं हुई), 1 का अर्थ है कि त्रुटि हुई:

shell-prompt> echo $?
output: 1

आप returnसब-शेल के अंदर स्टेटमेंट को निष्पादित करना चाहते हैं । जब returnबयान इसे चलाता है। । । कुंआ । । । रिटर्न। यदि आप इसे एक उप-शेल में निष्पादित करते हैं, तो यह आपकी स्क्रिप्ट से बाहर लौटने के बजाय, उस उप-शेल से वापस आ जाएगा। सब-शेल में निष्पादित करने के लिए, इसे इसमें लपेटें $(...):

shell-prompt> $(return >/dev/null 2>$1)

अब, आप उप-शेल का निकास कोड देख सकते हैं, जो 1 होना चाहिए, क्योंकि उप-शेल के अंदर एक त्रुटि उठाई गई थी:

shell-prompt> echo $?
output: 1

यह मेरे लिए 0.5.8-2.1ubuntu2$ readlink $(which sh) dash $ . test.sh This script is sourced. $ ./test.sh This script is sourced.
Phil Rutschman

3
POSIX यह निर्दिष्ट नहीं करता है returnकि शीर्ष स्तर ( pubs.opengroup.org/onlinepubs/9699919799/utilities/… ) पर क्या करना चाहिए । dashखोल व्यवहार करता है एक returnके रूप में शीर्ष स्तर पर exit। अन्य गोले शीर्ष स्तर पर पसंद bashया zshअनुमति नहीं देते हैं return, जो इस तरह की तकनीक की विशेषता है।
user5754163 12

यह श में काम करता है यदि आप $सबस्क्रिप्शन से पहले हटा दें । यही है, के (return >/dev/null 2>&1)बजाय का उपयोग करें $(return >/dev/null 2>&1)- लेकिन फिर यह काम में आना बंद हो जाता है।
नामस्रोत

@ नाम: चूंकि dash, यह समाधान काम नहीं करता है, shउदाहरण के लिए, Ubuntu पर कार्य करता है , इसलिए यह समाधान आम तौर पर काम नहीं करता है sh। समाधान मेरे लिए बश ३.२.५ does और ४.४.५ में काम करता है - $पहले या बिना (...)(हालांकि इसका कोई अच्छा कारण नहीं है $)।
mklement0

2
returnजब sourceएक खराब-एक्साइटेड कमांड के बाद स्क्रिप्ट को इनहिट किया जाता है, तो एक रिटर्न रिटर्न वेल तोड़ता है । एन्हांसमेंट एडॉप्ट किया।
डिमग

12

FWIW, अन्य सभी उत्तरों को पढ़ने के बाद, मैं मेरे लिए निम्नलिखित समाधान के साथ आया:

अद्यतन: वास्तव में, किसी ने एक और उत्तर में त्रुटि को सुधार दिया, जिसने मेरा भी प्रभावित किया। मुझे लगता है कि यहां अपडेट भी एक सुधार है (यदि आप उत्सुक हैं तो संपादन देखें)।

यह सभी लिपियों के लिए काम करता है, जो अलग-अलग गोले के साथ शुरू होता है और साथ#!/bin/bash ही कुछ जानकारी (जैसे सेटिंग्स) सीखने के लिए होता है, जो mainफ़ंक्शन के बाहर रखी जाती हैं ।

नीचे दी गई टिप्पणियों के अनुसार, यहां यह उत्तर स्पष्ट रूप से सभी bashप्रकारों के लिए काम नहीं करता है । सिस्टम के लिए भी नहीं, जहां /bin/shपर आधारित है bash। IE यह bashMacOS पर v3.x के लिए विफल रहता है । (Currenty मुझे नहीं पता कि इसे कैसे हल किया जाए।)

#!/bin/bash

# Function definitions (API) and shell variables (constants) go here
# (This is what might be interesting for other shells, too.)

# this main() function is only meant to be meaningful for bash
main()
{
# The script's execution part goes here
}

BASH_SOURCE=".$0" # cannot be changed in bash
test ".$0" != ".$BASH_SOURCE" || main "$@"

अंतिम 2 पंक्तियों के बजाय आप निम्नलिखित का उपयोग कर सकते हैं (मेरी राय में कम पठनीय) कोड BASH_SOURCEअन्य गोले में सेट नहीं करने के लिए और इसमें set -eकाम करने की अनुमति दें main:

if ( BASH_SOURCE=".$0" && exec test ".$0" != ".$BASH_SOURCE" ); then :; else main "$@"; fi

इस स्क्रिप्ट-रेसिपी में निम्नलिखित गुण हैं:

  • यदि bashसामान्य तरीके से निष्पादित किया mainजाता है , तो कहा जाता है। कृपया ध्यान दें कि इसमें एक कॉल शामिल नहीं है जैसे bash -x script(जिसमें scriptपथ शामिल नहीं है), नीचे देखें।

  • तो द्वारा sourced bash, mainकेवल, कहा जाता बुला स्क्रिप्ट एक ही नाम है करने के लिए होता है, तो है। (उदाहरण के लिए, यदि यह स्वयं या bash -c 'someotherscript "$@"' main-script args..जहां main-scriptहोना चाहिए, स्रोत के testरूप में जो देखता है $BASH_SOURCE)।

  • यदि evalकिसी अन्य के अलावा किसी अन्य द्वारा खट्टा / निष्पादित / पढ़ा / संपादित किया जाता है bash, mainतो उसे ( BASH_SOURCEहमेशा भिन्न होता है $0) नहीं कहा जाता है ।

  • mainयदि bashस्टड से स्क्रिप्ट पढ़ी जाती है, तब तक इसे नहीं कहा जाता है , जब तक कि आप $0खाली स्ट्रिंग को सेट नहीं करते हैं :( exec -a '' /bin/bash ) <script

  • यदि किसी अन्य स्क्रिप्ट के भीतर से ( सभी उद्धरण महत्वपूर्ण हैं! ) का मूल्यांकन किया bashजाता है । यदि सीधे कमांडलाइन से चलाया जाता है, तो यह पिछले मामले के समान है, जहां स्क्रिप्ट को स्टडिन से पढ़ा जाता है। ( रिक्त है, जबकि आमतौर पर अगर कुछ पूरी तरह से अलग करने के लिए मजबूर नहीं किया गया है।)evaleval "`cat script`" mainevalBASH_SOURCE$0/bin/bash

  • यदि mainनहीं कहा जाता है, तो यह रिटर्न true( $?=0) करता है ।

  • यह अप्रत्याशित व्यवहार पर निर्भर नहीं करता है (पहले मैंने अनिर्दिष्ट लिखा था, लेकिन मुझे ऐसा कोई दस्तावेज़ नहीं मिला, जिसे आप unsetBASH_SOURCEतो बदल सकते हैं और न ही बदल सकते हैं):

    • BASH_SOURCEएक बैश आरक्षित सरणी है । लेकिन BASH_SOURCE=".$0"इसे बदलने की अनुमति देने से कीड़े के एक बहुत खतरनाक डिब्बे खुल सकते हैं, इसलिए मेरी उम्मीद है, इसका कोई प्रभाव नहीं होना चाहिए (सिवाय, शायद, कुछ बदसूरत चेतावनी कुछ भविष्य के संस्करण में दिखाई देती है bash)।
    • कोई भी प्रलेखन नहीं है जो BASH_SOURCEबाहर के कार्यों को करता है। हालाँकि इसके विपरीत (कि यह केवल कार्यों में काम करता है) न तो प्रलेखित है। अवलोकन यह है कि यह काम करता है ( bashv4.3 और v4.4 के साथ परीक्षण किया गया , दुर्भाग्य से मेरे पास bashअब कोई v3.x नहीं है) और यह कि बहुत सारी स्क्रिप्ट्स टूट जाएंगी, अगर $BASH_SOURCEअवलोकन के रूप में काम करना बंद हो जाता है। इसलिए मेरी उम्मीद है, कि BASH_SOURCEभविष्य के संस्करणों के लिए bashभी रहता है।
    • इसके विपरीत (अच्छा लगता है, BTW!) पर विचार करें ( return 0 ), जो देता है 0अगर खट्टा और 1अगर नहीं खट्टा। यह न केवल मेरे लिए थोड़ा अप्रत्याशित आता है , और (रीडिंग के अनुसार) POSIX कहता है, कि returnउपधारा अपरिभाषित व्यवहार से है (और returnयहां स्पष्ट रूप से उपधारा से)। शायद इस सुविधा का अंततः इतना व्यापक उपयोग हो जाता है कि इसे और अधिक परिवर्तित नहीं किया जा सकता है, लेकिन AFAICS इस बात की अधिक संभावना है कि कुछ भविष्य के bashसंस्करण आकस्मिक रूप से उस मामले में वापसी व्यवहार को बदल दें।
  • दुर्भाग्य से bash -x script 1 2 3नहीं चलता है main(तुलना करें script 1 2 3जहां scriptकोई रास्ता नहीं है)। निम्नलिखित का उपयोग वर्कअराउंड के रूप में किया जा सकता है:

    • bash -x "`which script`" 1 2 3
    • bash -xc '. script' "`which script`" 1 2 3
    • जो bash script 1 2 3नहीं चलता mainउसे एक विशेषता माना जा सकता है।
  • ध्यान दें कि ( exec -a none script )कॉल main( bashइसे $0स्क्रिप्ट में पास नहीं करता है , इसके लिए आपको -cअंतिम बिंदु में दिखाए गए अनुसार उपयोग करने की आवश्यकता है )।

इस प्रकार, कुछ कोने के मामलों को छोड़कर, mainकेवल तभी कहा जाता है, जब स्क्रिप्ट को सामान्य तरीके से निष्पादित किया जाता है। आम तौर पर यह वही है, जो आप चाहते हैं, खासकर क्योंकि इसमें कोड को समझने के लिए जटिल कठिनता का अभाव है।

ध्यान दें कि यह पायथन कोड के समान है:

if __name__ == '__main__': main()

जो mainकि कुछ कोने के मामलों को छोड़कर, कॉलिंग को भी रोकता है , क्योंकि आप स्क्रिप्ट को आयात / लोड कर सकते हैं और इसे लागू कर सकते हैं__name__='__main__'

मुझे लगता है कि चुनौती को हल करने के लिए यह एक अच्छा सामान्य तरीका है

यदि आपके पास कुछ है, जिसे कई गोले द्वारा खट्टा किया जा सकता है, तो यह संगत होना चाहिए। हालाँकि (अन्य उत्तरों को पढ़ें), क्योंकि sourceआईएनजी का पता लगाने के लिए पोर्टेबल तरीके से लागू करने के लिए कोई आसान (लागू करना आसान) नहीं है , आपको नियमों को बदलना चाहिए

यह सुनिश्चित करके कि स्क्रिप्ट को निष्पादित किया जाना चाहिए /bin/bash, आप वास्तव में ऐसा करते हैं।

यह सभी मामलों को हल करता है लेकिन निम्नलिखित मामले में स्क्रिप्ट सीधे नहीं चल सकती है:

  • /bin/bash स्थापित या विघटनकारी नहीं है (एक बूट वातावरण में i। ई)
  • यदि आप इसे एक खोल में पाइप की तरह curl https://example.com/script | $SHELL
  • (ध्यान दें: यह केवल तभी सच है जब आपका bashहाल ही में पर्याप्त है। यह नुस्खा कुछ विशेष प्रकारों के लिए विफल होने की सूचना है। इसलिए यह जांचना सुनिश्चित करें कि यह आपके मामले के लिए काम करता है।)

हालाँकि मैं किसी भी वास्तविक कारण के बारे में नहीं सोच सकता, जहाँ आपको उसकी आवश्यकता हो और समान स्क्रिप्ट को समान रूप से स्रोत करने की क्षमता हो! आमतौर पर आप इसे mainहाथ से निष्पादित करने के लिए लपेट सकते हैं । उसके जैसा:

  • $SHELL -c '. script && main'
  • { curl https://example.com/script && echo && echo main; } | $SHELL
  • $SHELL -c 'eval "`curl https://example.com/script`" && main'
  • echo 'eval "`curl https://example.com/script`" && main' | $SHELL

टिप्पणियाँ

  • यह उत्तर अन्य सभी उत्तरों की सहायता के बिना संभव नहीं होता! यहां तक ​​कि गलत वाले - जो शुरू में मुझे यह पोस्ट कर रहे थे।

  • अद्यतन: https://stackoverflow.com/a/28776166/490291 में मिली नई खोजों के कारण संपादित


Ksh और bash-4.3 के लिए परीक्षण किया गया। अच्छा लगा। यह ऐसी दया है कि आपके उत्तर में एक कठिन जीवन दिया जाएगा कि अन्य उत्तरों में पहले से ही अप-वोट एकत्र करने का वर्ष था।
हैगेलो

इस उत्तर के लिए धन्यवाद। मैंने IF स्टेटमेंट के साथ लंबे समय तक 'कम पठनीय' परीक्षण की सराहना की, क्योंकि कम से कम गैर-मौन विफलता देने के लिए दोनों स्थितियों को संभालना अच्छा है। मेरे मामले में मुझे स्रोत का उपयोग न करने के लिए स्क्रिप्ट की आवश्यकता होती है या अन्यथा उन्हें अपनी त्रुटि के बारे में सूचित करना चाहिए।
टिम रिचर्डसन

@ टिनो: जैसा कि "अलग-अलग गोले के साथ ही खट्टा हो सकता है": MacOS पर, जहां /bin/shप्रभावी रूप bashसे POSIX मोड में है, आपकी स्क्रिप्ट को BASH_SOURCE तोड़ने के लिए असाइन करता है । अन्य गोले में ( dash, ksh, zsh), एक फ़ाइल तर्क के रूप में यह पारित करके अपनी स्क्रिप्ट लागू खोल निष्पादन के लिए सीधे खराबी (जैसे, zsh <your-script>अपनी स्क्रिप्ट गलती से लगता है कि यह है कर देगा sourced )। (आप पहले से ही है कि उल्लेख पाइप कोड खराबी, सभी गोले में।)
mklement0

@Tino: एक तरफ के रूप में: जबकि . <your-script>(सोर्सिंग) सभी POSIX- जैसे गोले से काम करता है सिद्धांत रूप में, यह केवल तभी समझ में आता है यदि स्क्रिप्ट में स्पष्ट रूप से POSIX सुविधाओं का उपयोग करने के लिए लिखा गया था, ताकि एक शेल से विशिष्ट विशेषताओं को तोड़ने से रोका जा सके अन्य गोले में; एक बैश शबंग रेखा (बजाय #!/bin/sh) का उपयोग करना इसलिए भ्रमित करना है - कम से कम एक विशिष्ट टिप्पणी के बिना। इसके विपरीत, यदि आपकी स्क्रिप्ट का अर्थ केवल बैश से चलाया जाना है (भले ही केवल इस बात पर विचार न करके कि क्या विशेषताएं पोर्टेबल नहीं हो सकती हैं), तो गैर-बैश के गोले में निष्पादन से इनकार करना बेहतर है ।
mklement0

1
@ mklement0 फिर से धन्यवाद, एक नोट जोड़ा कि कोई समस्या है। अन्य पाठकों के लिए: जब बैश v3.x के साथ खटास आती है main, तो इसे निष्पादित नहीं किया जाना चाहिए , लेकिन यह इस मामले में ऐसा करता है! और जब खट्टा हो जाता है /bin/sh, जो है bash --posix, वही इस मामले में होता है, और यह सादा गलत भी है।
टीनो

6

यह स्क्रिप्ट में बाद में काम करता है और _ _ चर पर निर्भर नहीं करता है:

## Check to make sure it is not sourced:
Prog=myscript.sh
if [ $(basename $0) = $Prog ]; then
   exit 1  # not sourced
fi

या

[ $(basename $0) = $Prog ] && exit

1
मुझे लगता है कि यह जवाब यहां कुछ POSIX कंप्लेंट में से एक है। यह स्पष्ट होने के साथ कि आपको फ़ाइल नाम जानना है और यह काम नहीं करता है यदि दोनों लिपियों में एक ही फ़ाइल नाम है।
जेपीजेड

5

मैं BASH- विशिष्ट उत्तर दूंगा। कोर्न शेल, सॉरी। मान लीजिए आपका स्क्रिप्ट नाम है include2.sh; फिर एक समारोह बनाने के अंदरinclude2.sh बुलाया am_I_sourced। यहाँ मेरा डेमो संस्करण है include2.sh:

am_I_sourced()
{
  if [ "${FUNCNAME[1]}" = source ]; then
    if [ "$1" = -v ]; then
      echo "I am being sourced, this filename is ${BASH_SOURCE[0]} and my caller script/shell name was $0"
    fi
    return 0
  else
    if [ "$1" = -v ]; then
      echo "I am not being sourced, my script/shell name was $0"
    fi
    return 1
  fi
}

if am_I_sourced -v; then
  echo "Do something with sourced script"
else
  echo "Do something with executed script"
fi

अब इसे कई तरीकों से निष्पादित करने का प्रयास करें:

~/toys/bash $ chmod a+x include2.sh

~/toys/bash $ ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ bash ./include2.sh 
I am not being sourced, my script/shell name was ./include2.sh
Do something with executed script

~/toys/bash $ . include2.sh
I am being sourced, this filename is include2.sh and my caller script/shell name was bash
Do something with sourced script

तो यह अपवाद के बिना काम करता है, और यह भंगुर $_सामान का उपयोग नहीं कर रहा है । यह ट्रिक BASH की आत्मनिरीक्षण सुविधा का उपयोग करती है, यानी बिल्ट-इन वैरिएबल FUNCNAMEऔर BASH_SOURCE; बैश मैनुअल पेज में उनके प्रलेखन देखें।

केवल दो चेतावनी:

1) कॉल करने के लिए am_I_called करना होगा जगह ले में sourced स्क्रिप्ट है, लेकिन भीतर नहीं किसी भी समारोह, ऐसा न हो कि ${FUNCNAME[1]}किसी और रिटर्न कुछ। हाँ ... आप जाँच कर सकते हैं ${FUNCNAME[2]}- लेकिन आप सिर्फ अपने जीवन को कठिन बनाते हैं।

2) फ़ंक्शन am_I_called को यदि आप यह जानना चाहते हैं कि फ़ाइल का नाम शामिल किया जा रहा है, तो फ़ंक्शन को आवश्यक स्क्रिप्ट में रहना चाहिए


1
स्पष्टता: इस सुविधा को काम करने के लिए BASH संस्करण 3+ की आवश्यकता होती है। BASH 2 में, FUNCNAME एक सरणी के बजाय एक स्केलर चर है। इसके अलावा BASH 2 में BASH_SOURCE सरणी चर नहीं है।
ववराण पुरवंतो

4

मैं डेनिस के बहुत ही उपयोगी उत्तर के लिए एक छोटे से सुधार का सुझाव देना चाहूंगा , इसे थोड़ा और पोर्टेबल बनाने के लिए, मुझे उम्मीद है:

[ "$_" != "$0" ] && echo "Script is being sourced" || echo "Script is a subshell"

क्योंकि [[(कुछ हद तक गुदा प्रतिगामी IMHO) डेबियन पोसिक्स संगत शेल द्वारा मान्यता प्राप्त नहीं है dash। इसके अलावा, किसी को रिक्त स्थान वाले फ़ाइलनामों से बचाने के लिए उद्धरणों की आवश्यकता हो सकती है, फिर से उक्त शेल में।


2

$_काफी भंगुर है। आपको इसे स्क्रिप्ट में पहली चीज के रूप में जांचना होगा। और फिर भी, यह आपके शेल (यदि खट्टा है) या स्क्रिप्ट का नाम (यदि निष्पादित किया गया है) का नाम रखने की गारंटी नहीं है।

उदाहरण के लिए, यदि उपयोगकर्ता ने सेट किया है BASH_ENV, तो एक स्क्रिप्ट के शीर्ष पर, स्क्रिप्ट $_में निष्पादित अंतिम कमांड का नाम है BASH_ENV

सबसे अच्छा तरीका मैंने पाया है $0कि इस तरह का उपयोग करें:

name="myscript.sh"

main()
{
    echo "Script was executed, running main..."
}

case "$0" in *$name)
    main "$@"
    ;;
esac

दुर्भाग्य से, यह तरीका zsh में बॉक्स से बाहर काम नहीं करता है क्योंकि functionargzeroविकल्प अपने नाम से अधिक कर रहा है, और डिफ़ॉल्ट रूप से होने के कारण।

इस के आसपास काम करने के लिए, मैं अपने unsetopt functionargzeroमें डाल दिया .zshenv


1

मैंने mklement0 कॉम्पैक्ट अभिव्यक्ति का पालन ​​किया ।

यह साफ है, लेकिन मैंने देखा कि यह ksh के मामले में विफल हो सकता है जब इसे इस तरह से लागू किया जाए:

/bin/ksh -c ./myscript.sh

(यह सोचता है कि यह खट्टा है और यह इसलिए नहीं है क्योंकि यह एक उपधारा निष्पादित करता है) लेकिन अभिव्यक्ति इस का पता लगाने के लिए काम करेगी:

/bin/ksh ./myscript.sh

इसके अलावा, भले ही अभिव्यक्ति कॉम्पैक्ट हो, वाक्यविन्यास सभी गोले के साथ संगत नहीं है।

इसलिए मैंने निम्नलिखित कोड के साथ समाप्त किया, जो बैश, zsh, डैश और ksh के लिए काम करता है

SOURCED=0
if [ -n "$ZSH_EVAL_CONTEXT" ]; then 
    [[ $ZSH_EVAL_CONTEXT =~ :file$ ]] && SOURCED=1
elif [ -n "$KSH_VERSION" ]; then
    [[ "$(cd $(dirname -- $0) && pwd -P)/$(basename -- $0)" != "$(cd $(dirname -- ${.sh.file}) && pwd -P)/$(basename -- ${.sh.file})" ]] && SOURCED=1
elif [ -n "$BASH_VERSION" ]; then
    [[ $0 != "$BASH_SOURCE" ]] && SOURCED=1
elif grep -q dash /proc/$$/cmdline; then
    case $0 in *dash*) SOURCED=1 ;; esac
fi

विदेशी गोले समर्थन जोड़ने के लिए स्वतंत्र महसूस :)


में ksh 93+u, ksh ./myscript.shमेरे लिए (मेरे कथन के साथ) ठीक काम करता है - आप किस संस्करण का उपयोग कर रहे हैं?
mklement0

मुझे डर है कि कोई निश्चित रूप से यह निर्धारित करने का कोई तरीका नहीं है कि क्या किसी स्क्रिप्ट को केवल POSIX-features का उपयोग करके खट्टा किया जा रहा है: आपका प्रयास लिनक्स ( /proc/$$/cmdline) को मानता है और dashकेवल उसी पर ध्यान केंद्रित करता है (जो shउदाहरण के लिए उबंटू पर भी काम करता है )। यदि आप कुछ मान्यताओं के लिए तैयार हैं, तो आप $0एक उचित - लेकिन अपूर्ण - परीक्षण की जांच कर सकते हैं जो पोर्टेबल है।
mklement0

मूल दृष्टिकोण के लिए ++, हालांकि - मैंने अपने जवाब के लिए एक परिशिष्ट में sh/ dashसाथ ही, समर्थन के रूप में सबसे अच्छा पोर्टेबल सन्निकटन के लिए इसे अनुकूलित करने के लिए स्वतंत्रता ली है ।
mklement0

0

मुझे नहीं लगता कि ksh और bash दोनों में ऐसा करने का कोई पोर्टेबल तरीका है। बैश में आप callerआउटपुट का उपयोग करके इसका पता लगा सकते हैं , लेकिन मुझे नहीं लगता कि ksh में कोई समान मौजूद है।


$0काम करता है bash, में ksh93, और pdksh। मुझे ksh88परीक्षण नहीं करना है।
मिकेल

0

मुझे एक वन-लाइनर की आवश्यकता है जो bash.version> = 3 के साथ [mac, linux] पर काम करता है और इनमें से कोई भी उत्तर बिल में फिट नहीं होता है।

[[ ${BASH_SOURCE[0]} = $0 ]] && main "$@"

1
bashसमाधान अच्छी तरह से काम करता है (आप के लिए आसान बनाने में कर सकता है $BASH_SOURCE), लेकिन kshसमाधान मजबूत नहीं है: आपकी स्क्रिप्ट द्वारा sourced किया जा रहा है एक और स्क्रिप्ट , आप एक झूठी सकारात्मक मिल जाएगा।
mklement0

0

इस बिंदु पर सीधे: आपको मूल्यांकन करना होगा कि चर "$ 0" आपके शेल के नाम के बराबर है या नहीं।


ऐशे ही:

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi


शैल :

$ bash check_source.sh 
First Parameter: check_source.sh

The script WAS NOT sourced.

स्रोत :

$ source check_source.sh
First Parameter: bash

The script was sourced.



यदि स्क्रिप्ट स्क्रिप्ट में थी या नहीं, तो यह पता लगाना 100% पोर्टेबल तरीका होना बहुत मुश्किल है ।

मेरे अनुभव के बारे में (Shellscripting साथ 7 साल) , केवल सुरक्षित तरीका (साथ वातावरण चर पर निर्भर नहीं PIDs और इतने पर है, जो तथ्य यह है कि यह कुछ है करने के लिए सुरक्षित वजह से नहीं है VARIABLE ), आपको:

  • अपने से संभावनाओं का विस्तार करें
  • यदि आप चाहते हैं, तो स्विच / केस का उपयोग करना।

दोनों विकल्पों को ऑटो स्केल नहीं किया जा सकता है, लेकिन यह सुरक्षित तरीका है।



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

जब आप SSH सत्र के माध्यम से एक स्क्रिप्ट का स्रोत बनाते हैं, तो चर "$ 0" ( स्रोत का उपयोग करते समय ), -bash द्वारा लौटाया गया मान होता है

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" || "$0" == "-bash" ]] ; then
    echo "The script was sourced."
else
    echo "The script WAS NOT sourced."
fi

या

#!/bin/bash

echo "First Parameter: $0"
echo
if [[ "$0" == "bash" ]] ; then
    echo "The script was sourced."
elif [[ "$0" == "-bash" ]] ; then
    echo "The script was sourced via SSH session."
else
    echo "The script WAS NOT sourced."
fi

2
डाउनवोट किया गया, क्योंकि यह सादा गलत है: /bin/bash -c '. ./check_source.sh'देता है The script WAS NOT sourced.। वही बग: ln -s /bin/bash pumuckl; ./pumuckl -c '. ./check_source.sh'->The script WAS NOT sourced.
टीनो

2
आपके पतन ने पूरे परिदृश्य को बदल दिया है और एक महान योगदान दिया है, टिनो। धन्यवाद!
ivanleoncz 22

0

मैं जाँच के साथ समाप्त हुआ [[ $_ == "$(type -p "$0")" ]]

if [[ $_ == "$(type -p "$0")" ]]; then
    echo I am invoked from a sub shell
else
    echo I am invoked from a source command
fi

जब curl ... | bash -s -- ARGSदूरस्थ स्क्रिप्ट को ऑन-द-फ्लाई चलाने के लिए उपयोग किया जाता है , तो वास्तविक स्क्रिप्ट फ़ाइल चलाने पर $ 0 bashसामान्य के बजाय होगा /bin/bash, इसलिए मैं type -p "$0"बैश का पूर्ण पथ दिखाने के लिए उपयोग करता हूं ।

परीक्षा:

curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath | bash -s -- /a/b/c/d/e /a/b/CC/DD/EE

source <(curl -sSL https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath)
relpath /a/b/c/d/e /a/b/CC/DD/EE

wget https://github.com/jjqq2013/bash-scripts/raw/master/common/relpath
chmod +x relpath
./relpath /a/b/c/d/e /a/b/CC/DD/EE

0

यह "सार्वभौमिक" क्रॉस शेल समर्थन के बारे में कुछ अन्य उत्तरों से अलग है। यह विशेष रूप से https://stackoverflow.com/a/2942183/3220983 के समान है , हालांकि थोड़ा अलग है। इस के साथ कमजोरी, यह है कि एक क्लाइंट स्क्रिप्ट का सम्मान करना चाहिए कि इसका उपयोग कैसे करें (यानी पहले एक चर निर्यात करके)। ताकत यह है कि यह सरल है और "कहीं भी" काम करना चाहिए। यहां आपके कट और पेस्ट खुशी के लिए एक टेम्पलेट है:

# NOTE: This script may be used as a standalone executable, or callable library.
# To source this script, add the following *prior* to including it:
# export ENTRY_POINT="$0"

main()
{
    echo "Running in direct executable context!"
}

if [ -z "${ENTRY_POINT}" ]; then main "$@"; fi

नोट: मैं उपयोग करता हूं exportबस यह सुनिश्चित करें कि इस तंत्र को उप प्रक्रियाओं में बढ़ाया जा सकता है।

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