"-O इरेट्रेस" (यानी सेट-ई) का उपयोग करके कमांड प्रतिस्थापन में त्रुटियों को फँसाना


14

इस रेफरी मैनुअल के अनुसार :

-ई (भी इरेट्रेस)

यदि सेट किया गया है, तो ERR पर कोई भी जाल शेल फ़ंक्शन, कमांड सब्स्टिट्यूशंस और सब-डिहेल्ड वातावरण में निष्पादित कमांड द्वारा विरासत में मिला है। ईआरआर ट्रैप आमतौर पर ऐसे मामलों में विरासत में नहीं मिला है।

हालाँकि, मुझे इसकी गलत व्याख्या करनी चाहिए, क्योंकि निम्नलिखित काम नहीं करता है:

#!/usr/bin/env bash
# -*- bash -*-

set -e -o pipefail -o errtrace -o functrace

function boom {
  echo "err status: $?"
  exit $?
}
trap boom ERR


echo $( made up name )
echo "  ! should not be reached ! "

मैं पहले से ही सरल असाइनमेंट जानता हूं, my_var=$(made_up_name)स्क्रिप्ट के साथ set -e(यानी एरेक्सिट) से बाहर निकल जाएगा ।

क्या -E/-o errtraceउपरोक्त कोड की तरह काम करना चाहिए? या, सबसे अधिक संभावना है, मैंने इसे गलत बताया?


2
यह अच्छा प्रश्न है। वांछित व्यवहार के echo $( made up name )साथ प्रतिस्थापित करना $( made up name )। हालांकि मेरे पास कोई स्पष्टीकरण नहीं है।
इरिवर

मैं बैश-ई के बारे में नहीं जानता, लेकिन मुझे पता है कि-केवल एक शेल निकास से प्रभाव पड़ता है अगर त्रुटि एक पाइपलाइन में अंतिम कमांड से होती है। तो आपके var=$( pipe )और $( pipe )उदाहरण दोनों पाइप एंडपॉइंट्स का प्रतिनिधित्व pipe > echoकरेंगे जबकि ऐसा नहीं होगा। मेरा मैन पेज कहता है: "1. मल्टी-कमांड पाइप लाइन में किसी भी व्यक्तिगत कमांड की विफलता शेल को बाहर निकलने का कारण नहीं
बनेगी

हालांकि आप इसे विफल बना सकते हैं: इको $ ($ {madeupname?})। लेकिन वह सेट है -ई। फिर से, -E मेरे अपने अनुभव से बाहर है।
मिकसेर

@mikeserv @ 1_CR बैश मैनुअल @ इको इंगित करता है कि echoहमेशा रिटर्न 0. यह विश्लेषण में

जवाबों:


5

ध्यान दें: zsh"खराब पैटर्न" के बारे में शिकायत करेंगे यदि आप इसे यहां के अधिकांश उदाहरणों के लिए "इनलाइन टिप्पणियों" को स्वीकार करने के लिए कॉन्फ़िगर नहीं करते हैं और उन्हें प्रॉक्सी शेल के माध्यम से नहीं चलाते हैं जैसा मैंने किया है sh <<-\CMD

ठीक है, इसलिए, जैसा कि मैंने उपरोक्त टिप्पणियों में कहा था, मुझे विशेष रूप से बैश केset -E बारे में नहीं पता है , लेकिन मुझे पता है कि पॉसिक्स संगत गोले यदि आप चाहें तो एक मूल्य का परीक्षण करने का एक सरल साधन प्रदान करते हैं:

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
        echo "echo still works" 
    }
    _test && echo "_test doesnt fail"
    # END
    CMD
sh: line 1: empty: error string
+ echo

+ echo 'echo still works'
echo still works
+ echo '_test doesnt fail'
_test doesnt fail

ऊपर आप देखेंगे कि हालांकि मैं इस्तेमाल किया parameter expansionपरीक्षण करने के लिए ${empty?} _test()अभी भी returnहै एक पास - के रूप में दिखाई है में पिछले echoयह तब होता है कारण विफल मूल्य मारता है $( command substitution )subshell है कि यह शामिल है, लेकिन इसकी मूल खोल - _testइस समय - ट्रकिंग रहती है। और echoपरवाह नहीं करता - केवल सेवा करने के लिए यह बहुत खुश एक \newline; echoहै नहीं एक परीक्षण।

लेकिन इस पर विचार करें:

    sh -evx <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    _test ||\
            echo "this doesnt even print"
    # END
    CMD
_test+ sh: line 1: empty: function doesnt run

चूँकि मैंने अब _test()'sपूर्व-निर्धारित पैरामीटर के साथ इनपुट को फीड किया है, INIT here-documentइसलिए _test()फ़ंक्शन बिल्कुल भी चलाने का प्रयास नहीं करता है। क्या अधिक है कि shखोल स्पष्ट रूप से भूत को पूरी तरह से छोड़ देता है और echo "this doesnt even print" प्रिंट भी नहीं करता है।

संभवतः वह नहीं है जो आप चाहते हैं।

ऐसा इसलिए होता है क्योंकि ${var?}शैली पैरामीटर-विस्तार को लापता पैरामीटर की स्थिति में छोड़ने के लिए डिज़ाइन किया गया हैshell , यह इस तरह से काम करता है :

${parameter:?[word]}

संकेत त्रुटि यदि Nullया Unset.पैरामीटर अशांत या अशक्त है, expansion of wordया (यह दर्शाता है कि यह संदेश अप्रभावित है यदि शब्द छोड़ा गया है) written to standard errorऔर होगा shell exits with a non-zero exit statusअन्यथा, का मूल्य parameter shall be substitutedएक इंटरैक्टिव शेल से बाहर निकलने की आवश्यकता नहीं है।

मैं पूरे दस्तावेज़ को कॉपी / पेस्ट नहीं करूंगा, लेकिन यदि आप किसी ऐसे set but nullमान के लिए विफलता चाहते हैं जो आप फ़ॉर्म का उपयोग करते हैं:

${var : error message }

साथ :colonऊपर के रूप में। यदि आप nullसफल होने के लिए एक मूल्य चाहते हैं , तो बृहदान्त्र को छोड़ दें। आप इसे नकार भी सकते हैं और केवल सेट वैल्यू के लिए असफल हो सकते हैं, जैसा कि मैं एक पल में दिखाता हूं।

का एक और रन _test():

    sh <<-\CMD
    _test() { echo $( ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?function doesnt run}
    INIT
    echo "this runs" |\
        ( _test ; echo "this doesnt" ) ||\
            echo "now it prints"
    # END
    CMD
this runs
sh: line 1: empty: function doesnt run
now it prints

यह सभी प्रकार के त्वरित परीक्षणों के साथ काम करता है, लेकिन ऊपर आप देखेंगे कि _test(), विफलताओं के बीच से भागते हैं pipeline, और वास्तव में इसकी command listउप-समयावधि पूरी तरह से विफल हो जाती है, क्योंकि फ़ंक्शन के भीतर कोई भी कमांड न तो चलती है और न ही निम्नलिखित echoचलती है। हालांकि यह भी दिखाया गया है कि यह आसानी से परीक्षण किया जा सकता है क्योंकि echo "now it prints" अब प्रिंट करता है।

शैतान विवरण में है, मुझे लगता है। उपरोक्त मामले में, जो शेल बाहर निकलता है वह स्क्रिप्ट नहीं है , _main | logic | pipelineलेकिन ( subshell in which we ${test?} ) ||थोड़ा सैंडबॉक्सिंग के लिए कहा जाता है।

और यह स्पष्ट नहीं हो सकता है, लेकिन अगर आप केवल विपरीत मामले, या केवल set=मूल्यों के लिए पास करना चाहते हैं, तो यह काफी सरल है:

    sh <<-\CMD
    N= #N is NULL
    _test=$N #_test is also NULL and
    v="something you would rather do without"    
    ( #this subshell dies
        echo "v is ${v+set}: and its value is ${v:+not NULL}"
        echo "So this ${_test:-"\$_test:="} will equal ${_test:="$v"}"
        ${_test:+${N:?so you test for it with a little nesting}}
        echo "sure wish we could do some other things"
    )
    ( #this subshell does some other things 
        unset v #to ensure it is definitely unset
        echo "But here v is ${v-unset}: ${v:+you certainly wont see this}"
        echo "So this ${_test:-"\$_test:="} will equal NULL ${_test:="$v"}"
        ${_test:+${N:?is never substituted}}
        echo "so now we can do some other things" 
    )
    #and even though we set _test and unset v in the subshell
    echo "_test is still ${_test:-"NULL"} and ${v:+"v is still $v"}"
    # END
    CMD
v is set: and its value is not NULL
So this $_test:= will equal something you would rather do without
sh: line 7: N: so you test for it with a little nesting
But here v is unset:
So this $_test:= will equal NULL
so now we can do some other things
_test is still NULL and v is still something you would rather do without

उपरोक्त उदाहरण POSIX पैरामीटर प्रतिस्थापन के सभी 4 रूपों और उनके विभिन्न :colon nullया not nullपरीक्षणों का लाभ उठाता है । उपरोक्त लिंक में अधिक जानकारी है, और यहां यह फिर से है

और मुझे लगता है कि हमें अपने _testकार्य को भी दिखाना चाहिए , है ना? हम सिर्फ empty=somethingअपने फ़ंक्शन के लिए एक पैरामीटर के रूप में घोषणा करते हैं (या किसी भी समय पहले से):

    sh <<-\CMD
    _test() { echo $( echo ${empty:?error string} ) &&\
            echo "echo still works" ; } 2<<-INIT
            ${empty?tested as a pass before function runs}
    INIT
    echo "this runs" >&2 |\
        ( empty=not_empty _test ; echo "yay! I print now!" ) ||\
            echo "suspiciously quiet"
    # END
    CMD
this runs
not_empty
echo still works
yay! I print now!

यह ध्यान दिया जाना चाहिए कि यह मूल्यांकन अकेले खड़ा है - इसे विफल करने के लिए कोई अतिरिक्त परीक्षण की आवश्यकता नहीं है। कुछ और उदाहरण:

    sh <<-\CMD
    empty= 
    ${empty?null, no colon, no failure}
    unset empty
    echo "${empty?this is stderr} this is not"
    # END
    CMD
sh: line 3: empty: this is stderr

    sh <<-\CMD
    _input_fn() { set -- "$@" #redundant
            echo ${*?WHERES MY DATA?}
            #echo is not necessary though
            shift #sure hope we have more than $1 parameter
            : ${*?WHERES MY DATA?} #: do nothing, gracefully
    }
    _input_fn heres some stuff
    _input_fn one #here
    # shell dies - third try doesnt run
    _input_fn you there?
    # END
    CMD
heres some stuff
one
sh: line :5 *: WHERES MY DATA?

और इसलिए अंत में हम मूल प्रश्न पर वापस आते हैं: कैसे एक $(command substitution)उपधारा में त्रुटियों को संभालना है ? सच्चाई यह है - दो तरीके हैं, लेकिन न तो प्रत्यक्ष है। समस्या का मूल शेल के मूल्यांकन की प्रक्रिया है - शेल के मूल्यांकन की प्रक्रिया में शेल विस्तार ( $(command substitution)पहले) वर्तमान शेल कमांड के निष्पादन की तुलना में होता है - जो तब होता है जब आपकी त्रुटियां पकड़ी जा सकती हैं और फंस सकती हैं।

सेशन के अनुभवों का मुद्दा यह है कि जब तक मौजूदा शेल त्रुटियों के लिए मूल्यांकन करता है, $(command substitution)तब तक सब-सब पहले ही हटा दिया गया है - कोई त्रुटि नहीं है।

तो दो तरीके क्या हैं? या तो आप इसे स्पष्ट $(command substitution)रूप से परीक्षणों के साथ उपधारा के भीतर करते हैं जैसा कि आप इसके बिना करेंगे, या आप इसके परिणामों को वर्तमान शेल चर में अवशोषित करेंगे और इसके मूल्य का परीक्षण करेंगे।

विधि 1:

    echo "$(madeup && echo \: || echo '${fail:?die}')" |\
          . /dev/stdin

sh: command not found: madeup
/dev/stdin:1: fail: die

    echo $?

126

विधि 2:

    var="$(madeup)" ; echo "${var:?die} still not stderr"

sh: command not found: madeup
sh: var: die

    echo $?

1

यह प्रति पंक्ति घोषित चर की संख्या की परवाह किए बिना विफल हो जाएगा:

   v1="$(madeup)" v2="$(ls)" ; echo "${v1:?}" "${v2:?}"

sh: command not found: madeup
sh: v1: parameter not set

और हमारा रिटर्न मान स्थिर रहता है:

    echo $?
1

अब TRAP:

    trap 'printf %s\\n trap resurrects shell!' ERR
    v1="$(madeup)" v2="$(printf %s\\n shown after trap)"
    echo "${v1:?#1 - still stderr}" "${v2:?invisible}"

sh: command not found: madeup
sh: v1: #1 - still stderr
trap
resurrects
shell!
shown
after
trap

    echo $?
0

उदाहरण के लिए, व्यावहारिक रूप से उसका सटीक विनिर्देश: गूंज $ {v = $ (मेडुपनाम)}; प्रतिध्वनि "$ {v:? trap1st}! पहुंच नहीं है!"
मोसर्वि

1
संक्षेप में: ये पैरामीटर विस्तार यदि चर सेट किए जाते हैं तो नियंत्रित करने का एक शानदार तरीका है। गूंज की निकास स्थिति के संबंध में, मैंने देखा कि echo "abc" "${v1:?}"निष्पादित नहीं होता है (एबीसी कभी मुद्रित नहीं होता है)। और शेल वापस आता है। यह (यहां तक ​​कि "${v1:?}"सीधे क्ली पर) एक आदेश के साथ या इसके बिना सच है । लेकिन ओपी की स्क्रिप्ट के लिए, जाल को ट्रिगर करने के लिए आवश्यक सभी अपने चर असाइनमेंट को एक लाइन पर अकेले एक बिना किसी कमांड के प्रतिस्थापन के लिए रखा गया है। अन्यथा गूंज का व्यवहार हमेशा 0 वापस करना है, जब तक कि आपके द्वारा समझाए गए परीक्षणों के साथ बाधित न हो।

बस v=$( madeup )। मैं नहीं देखता कि उसके साथ क्या असुरक्षित है। यह सिर्फ एक असाइनमेंट है, व्यक्ति ने उदाहरण के लिए कमांड को गलत बताया v="$(lss)"। यह त्रुटी है। हां, आप अंतिम आदेश $ की त्रुटि स्थिति के साथ सत्यापित कर सकते हैं? - क्योंकि यह लाइन पर कमांड है (कमांड नाम के साथ एक असाइनमेंट) और कुछ नहीं - गूंजने के लिए तर्क नहीं। इसके अलावा, यहाँ यह फंक्शन के रूप में फंस गया है! = 0, ताकि आपको दो बार फीडबैक मिले। अन्यथा निश्चित रूप से जैसा कि आप समझाते हैं कि एक फ्रेमवर्क के भीतर इस क्रम को करने का एक बेहतर तरीका है, लेकिन ओपी में 1 एकल पंक्ति थी: एक प्रतिध्वनि और साथ ही उसका दूसरा प्रतिस्थापन। वह गूंज के बारे में सोच रहा था।

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

1
अच्छा बिंदु - शायद अधिक प्रयास के लिए कहा जाता है। बीमार इस पर सोचते हैं।
मिकसेर्व

1

यदि सेट किया गया है, तो ERR पर कोई जाल शेल फ़ंक्शन, कमांड प्रतिस्थापन और एक उप-वातावरण में निष्पादित कमांड द्वारा विरासत में मिला है

आपकी स्क्रिप्ट में इसका कमांड निष्पादन ( echo $( made up name )) है। बैश कमांड में या तो सीमांकित किया गया है ; या नई लाइन के साथ । आज्ञा में

echo $( made up name )

$( made up name )कमांड का एक हिस्सा माना जाता है। यहां तक ​​कि अगर यह हिस्सा विफल हो जाता है और त्रुटि के साथ लौटता है, तो पूरी कमांड सफलतापूर्वक निष्पादित होती है क्योंकि echoइसके बारे में पता नहीं है। कमांड के साथ 0 के साथ कोई ट्रैप ट्रिगर नहीं हुआ।

आपको इसे दो कमांड, असाइनमेंट और इको में डालने की आवश्यकता है

var=$(made_up_name)
echo $var

echo $varविफल नहीं होता है - वास्तव में इस पर एक नज़र पाने से $varपहले खाली करने के लिए विस्तारित किया echoजाता है।
mikeserv

0

यह बैश में एक बग के कारण है। कमांड प्रतिस्थापन चरण के दौरान

echo $( made up name )

madeएक उपधारा में रन (या पाया जाना विफल रहता है), लेकिन उपधारा "अनुकूलित" इस तरह से है कि यह मूल जाल से कुछ जाल का उपयोग नहीं करता है। यह संस्करण 4.4.5 में तय किया गया था :

कुछ परिस्थितियों में, एक साधारण आदेश एक कांटा को खत्म करने के लिए अनुकूलित है, जिसके परिणामस्वरूप एक EXIT जाल को निष्पादित नहीं किया जा रहा है।

4.4.5 या उच्चतर बैश के साथ, आपको निम्न आउटपुट देखना चाहिए:

error.sh: line 13: made: command not found
err status: 127
  ! should not be reached !

ट्रैप हैंडलर को उम्मीद के रूप में बुलाया गया है, फिर सबशेल बाहर निकलता है। ( set -eकेवल उपश्रेणियाँ बाहर निकलने का कारण बनती हैं, न कि अभिभावक, ताकि "तक नहीं पहुँचा जा सके" संदेश को, वास्तव में, पहुँचा जाना चाहिए।)

पुराने संस्करणों के लिए एक वर्कअराउंड एक पूर्ण, गैर-अनुकूलित उपधारा के निर्माण के लिए बाध्य है:

echo $( ( made up name ) )

अरिथमेटिक एक्सपेंशन से अलग स्पेस की आवश्यकता होती है।

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