स्क्रिप्ट से बाहर क्यों नहीं निकलता है?


48

मेरे पास एक स्क्रिप्ट है, जब मैं चाहता हूं कि इससे बाहर नहीं निकलता।

उसी त्रुटि के साथ एक उदाहरण स्क्रिप्ट है:

#!/bin/bash

function bla() {
    return 1
}

bla || ( echo '1' ; exit 1 )

echo '2'

मैं आउटपुट देखना पसंद करूंगा:

:~$ ./test.sh
1
:~$

लेकिन मैं वास्तव में देखता हूं:

:~$ ./test.sh
1
2
:~$

क्या ()कमांड चेनिंग किसी तरह स्कोप बनाता है? exitस्क्रिप्ट से नहीं तो क्या बाहर निकल रहा है?


5
यह एक शब्द का जवाब देने के लिए कहता है: उपधारा
यहोशू

जवाबों:


87

()उप-क्रम में कमांड चलाता है, इसलिए आपके द्वारा exitसदस्यता से बाहर निकलकर मूल शेल पर वापस आ रहा है। {}यदि आप वर्तमान शेल में कमांड चलाना चाहते हैं तो ब्रेसिज़ का उपयोग करें ।

बैश मैनुअल से:

(सूची) सूची को एक उप-वातावरण में निष्पादित किया जाता है। वैरिएबल असाइनमेंट और बिल्डइन कमांड जो शेल के वातावरण को प्रभावित करते हैं कमांड के पूरा होने के बाद प्रभाव में नहीं रहते हैं। वापसी की स्थिति सूची की निकास स्थिति है।

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

यह ध्यान देने योग्य है कि शेल सिंटैक्स काफी सुसंगत है और उप-भाग अन्य ()कंस्ट्रक्शन जैसे कमांड प्रतिस्थापन (पुरानी शैली के `..`सिंटैक्स के साथ) या प्रक्रिया प्रतिस्थापन में भी भाग लेता है , इसलिए निम्नलिखित वर्तमान शेल से बाहर नहीं निकलेगा:

echo $(exit)
cat <(exit)

हालांकि यह स्पष्ट हो सकता है कि उप-समूह शामिल हैं जब आदेशों को स्पष्ट रूप से अंदर रखा जाता है (), तो कम दिखाई देने वाला तथ्य यह है कि वे इन अन्य संरचनाओं में भी पैदा होते हैं:

  • बैकग्राउंड में कमांड शुरू हुई

    exit &

    वर्तमान शेल से बाहर नहीं निकलता क्योंकि (बाद में man bash)

    यदि नियंत्रण ऑपरेटर द्वारा एक कमांड को समाप्त किया जाता है और, शेल एक उपधारा में पृष्ठभूमि में कमांड को निष्पादित करता है। शेल कमांड के समाप्त होने की प्रतीक्षा नहीं करता है, और रिटर्न स्थिति 0 है।

  • पाइपलाइन

    exit | echo foo

    अभी भी केवल उपधारा से बाहर निकलता है।

    हालाँकि इस संबंध में अलग-अलग गोले अलग-अलग व्यवहार करते हैं। उदाहरण के लिए bash, पाइपलाइन के सभी घटकों को अलग-अलग उप-खंडों में रखा जाता है (जब तक कि आप lastpipeइनवोकेशन में विकल्प का उपयोग नहीं करते हैं जहां नौकरी नियंत्रण सक्षम नहीं है), लेकिन एटी एंड टी kshऔर zshवर्तमान शेल के अंदर अंतिम भाग चलाते हैं (दोनों व्यवहारों को पॉसिक्स द्वारा अनुमति दी जाती है)। इस प्रकार

    exit | exit | exit

    मूल रूप से बाश में कुछ भी नहीं करता है, लेकिन आखिरी के कारण चिड़ियाघर से बाहर निकलता है exit

  • coproc exitexitएक सबशेल में भी चलता है।


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

10
मैन पेज में रिक्ति का ध्यान रखें: {और }वाक्यविन्यास नहीं हैं, वे आरक्षित शब्द हैं और उन्हें रिक्त स्थान से घिरा होना चाहिए, और सूची को कमांड टर्मिनेटर (अर्धविराम, न्यूलाइन, एम्परसेंड) के साथ समाप्त होना चाहिए
ग्लेन जैकसन

ब्याज से, क्या यह वास्तव में एक अन्य प्रक्रिया या आंतरिक स्टैक में एक अलग वातावरण है? मैं chdirs को अलग करने के लिए बहुत () का उपयोग करता हूं, और अगर $ पूर्व आदि के मेरे उपयोग के साथ भाग्यशाली रहा होगा।
डैन शेपर्ड

5
@DanSheppard यह एक और प्रक्रिया है, लेकिन (echo $$)अभिभावक शेल आईडी को प्रिंट करता है क्योंकि सब्सक्रिप्शन बनने $$से पहले ही इसका विस्तार हो जाता है। वास्तव में मुद्रण उपधारा प्रक्रिया आईडी मुश्किल हो सकती है, देखें stackoverflow.com/questions/9119885/…
jimij

@ जिमीज, यह कैसे हो सकता है कि सबस्क्रिप्शन बनाने $$से पहले इसका विस्तार किया जाए, और फिर भी $BASHPIDएक सब-वेल्यू के लिए सही मान दिखाया जाए?
वाइल्डकार्ड

13

exitएक उपखंड में निष्पादन एक नुकसान है:

#!/bin/bash
function calc { echo 42; exit 1; }
echo $(calc)

स्क्रिप्ट 42 प्रिंट करता है, रिटर्न कोड के साथ सब्मिट से बाहर निकलता है 1, और स्क्रिप्ट के साथ जारी रहता है। यहां तक ​​कि कॉल की जगह लेने से भी echo $(CALC) || exit 1मदद नहीं मिलती है क्योंकि रिटर्न कोड की वापसी कोड की echoपरवाह किए बिना 0 है calc। और calcपहले निष्पादित किया जाता है echo

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

मैं एक फाइल बनाना चाहता हूं, जिसका नाम है "ईयर महीने डे।लॉग", यानी 20141211.logआज के लिए। दिनांक एक उपयोगकर्ता द्वारा इनपुट है जो उचित मूल्य प्रदान करने में विफल हो सकता है। इसलिए, मेरे फ़ंक्शन में fnameमैं dateउपयोगकर्ता इनपुट की वैधता को सत्यापित करने के लिए वापसी मूल्य की जांच करता हूं :

#!/bin/bash

doit ()
    {
    local FNAME=$(fname "$1") || exit 1
    touch "${FNAME}"
    }

fname ()
    {
    date +"%Y%m%d.log" -d"$1" 2>/dev/null
    if [ "$?" != 0 ] ; then
        echo "fname reports \"Illegal Date\"" >&2
        exit 1
    fi
    }

doit "$1"

अछा लगता है। स्क्रिप्ट को नाम दें s.sh। यदि उपयोगकर्ता स्क्रिप्ट को कॉल करता है ./s.sh "Thu Dec 11 20:45:49 CET 2014", तो फ़ाइल 20141211.logबनाई जाती है। यदि, हालांकि, उपयोगकर्ता प्रकार ./s.sh "Thu hec 11 20:45:49 CET 2014", तो स्क्रिप्ट आउटपुट:

fname reports "Illegal Date"
touch: cannot touch ‘’: No such file or directory

रेखा का fname…कहना है कि उप इनपुट में खराब इनपुट डेटा का पता लगाया गया है। लेकिन रेखा exit 1के अंत में local …कभी ट्रिगर नहीं किया जाता है क्योंकि localनिर्देश हमेशा लौटता है 0। ऐसा इसलिए है क्योंकि इसके बादlocal इसे निष्पादित किया जाता है और इस तरह इसके रिटर्न कोड को अधिलेखित कर दिया जाता है। और उसके कारण, स्क्रिप्ट जारी है और एक खाली पैरामीटर के साथ आह्वान करती है । यह उदाहरण सरल है, लेकिन एक वास्तविक एप्लिकेशन में बैश का व्यवहार काफी भ्रामक हो सकता है। मुझे पता है, असली प्रोग्रामर स्थानीय लोगों का उपयोग नहीं करते हैं $(fname)touch

यह स्पष्ट करने के लिए: इसके बिना local, स्क्रिप्ट अवैध रूप से गर्भपात करती है जब एक अवैध तारीख दर्ज की जाती है।

फिक्स लाइन को विभाजित करना है

local FNAME
FNAME=$(fname "$1") || exit 1

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

हालांकि बग नहीं होने के कारण मुझे लगता है कि बैश का व्यवहार उल्टा है। मैं निष्पादन के अनुक्रम से अवगत हूं, localएक टूटे हुए असाइनमेंट को मुखौटा नहीं करना चाहिए, फिर भी।

मेरे प्रारंभिक उत्तर में कुछ गलतियाँ थीं। Mikeserv के साथ एक खुलासा और गहन चर्चा के बाद (उसके लिए धन्यवाद) मैं उन्हें ठीक करने के लिए गया था।


@mikeserv: मैंने प्रासंगिकता दिखाने के लिए एक उदाहरण जोड़ा।
हरमनक

@ बाइक: हाँ, तुम सही हो। यहां तक ​​कि मरोड़ भी। लेकिन गड्ढे अभी भी हैं।
हरमनक

@ mikeserv: क्षमा करें, मेरा उदाहरण टूट गया था। में टेस्ट भूल गया doit()
२१:१४ बजे


2

वास्तविक समाधान:

#!/bin/bash

function bla() {
    return 1
}

bla || { echo '1'; exit 1; }

echo '2'

त्रुटि समूहिंग तभी निष्पादित होगी जब blaत्रुटि स्थिति लौटाती है, और exitएक उपधारा में नहीं है इसलिए पूरी स्क्रिप्ट बंद हो जाती है।


1

कोष्ठक एक उपधारा शुरू करते हैं और बाहर निकलने पर ही उपधारा निकल जाती है।

यदि आप फ़ाइल $?से बाहर निकल गए हैं तो आप स्क्रिप्ट से बाहर निकलने के लिए एक्ज़िटकोड को पढ़ सकते हैं और इसे अपनी स्क्रिप्ट में जोड़ सकते हैं :

#!/bin/bash

function bla() {
    return 1
}

bla || ( echo '1' ; exit 1 )

exitcode=$?
if [ $exitcode != 0 ]; then exit $exitcode; fi

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