2016-12-25 14 views
10

एक असफल पुनर्निर्देशन (गैर-मौजूद फ़ाइल या अपर्याप्त फ़ाइल पहुँच के कारण) पर, ErrorLevel मान सेट करने के लिए नहीं लगता है (निम्न उदाहरण में, फ़ाइल test.tmp है लिखना-सुरक्षित और फाइल test.nil मौजूद नहीं है):त्रुटि लेवल केवल के बाद क्यों सेट किया गया है || असफल पुनर्निर्देशन पर ऑपरेटर?

>>> (call) & rem // (reset `ErrorLevel`) 

>>> > "test.tmp" echo Text 
Access is denied. 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=0 

>>> (call) & rem // (reset `ErrorLevel`) 

>>> < "test.nil" set /P DUMMY="" 
The system cannot find the file specified. 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=0 

हालांकि, जैसे ही विफल रही पुनर्निर्देशन सशर्त संयोजन ऑपरेटर || है, जो बाहर निकलने के कोड से क्वेरी है द्वारा पीछा किया जाता, ErrorLevel1 करने के लिए सेट हो जाता है, अप्रत्याशित रूप से:

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (> "test.tmp" echo Text) || echo Fail 
Access is denied. 
Fail 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=1 

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (< "test.nil" set /P DUMMY="") || echo Fail 
The system cannot find the file specified. 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=1 

दिलचस्प बात यह है ErrorLevel रहता 0 जब ऑपरेटर && प्रयोग किया जाता है:

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (> "test.tmp" echo Text) && echo Pass 
Access is denied. 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=0 

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (< "test.nil" set /P DUMMY="") && echo Pass 
The system cannot find the file specified. 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=0 

ErrorLevel ऑपरेटर & का उपयोग कर भी 0 रहता है:

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (> "test.tmp" echo Text) & echo Pass or Fail 
Access is denied. 
Pass or Fail 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=0 

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (< "test.nil" set /P DUMMY="") & echo Pass or Fail 
The system cannot find the file specified. 
Pass or Fail 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=0 

दोनों सशर्त संयोजन ऑपरेटरों && और || दिखाई मामले में, ErrorLevel सेट किया गया है 1 पर भी (यदि ||&& से पहले होता है, तो दोनों शाखाओं को निष्पादित किया जाता है पिछले उदाहरण है, लेकिन मैं यह सिर्फ इसलिए && पूर्ववर्ती echo आदेश के निकास कोड) का मूल्यांकन करता है लगता है:

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (> "test.tmp" echo Text) && echo Pass || echo Fail 
Access is denied. 
Fail 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=1 

>>> (call) & rem // (reset `ErrorLevel`) 

>>> (< "test.nil" set /P DUMMY="") || echo Fail && echo Pass 
The system cannot find the file specified. 
Fail 
Pass 

>>> echo ErrorLevel=%ErrorLevel% 
ErrorLevel=1 

तो ErrorLevel मूल्य और || ऑपरेटर, क्यों ErrorLevel|| से प्रभावित होता है के बीच संबंध क्या है? || बाहर निकलने के कोड को ErrorLevel पर कॉपी कर रहा है? क्या यह सब केवल (असफल) पुनर्निर्देशन के साथ संभव है, क्योंकि किसी भी आदेश को निष्पादित करने से पहले इसे संभाला जाता है? && द्वारा ErrorLevel0 को रीसेट किया जा रहा - -

इससे भी अधिक अजीब है, मैं विपरीत व्यवहार का पालन नहीं कर सकता है, सही ढंग से परीक्षण सेटअप को वापस लाने में जब (जो है, (call)(call) द्वारा की जगह (शुरू ErrorLevel1 करने के लिए सेट करने के लिए), फ़ाइल test.tmp की केवल पढ़ने के लिए विशेषता समाशोधन, फ़ाइल test.nil (पहली पंक्ति खाली नहीं set /P से बचने के लिए 1 को ErrorLevel स्थापित करने के लिए) बनाने, और परीक्षण के लिए फाइल एक्सटेंशन .bat बजाय .cmd का उपयोग कर (set /P से बचने के लिए ErrorLevel0 को पुनर्स्थापित करने के लिए))।

मैं विंडोज 7 और विन्डोज़ 10

+2

मैं एक siilar सवाल पूछने के रास्ते पर था जब मुझे पता चला कि इसमें से अधिकांश पहले से ही [dbenham द्वारा कवर किया गया था] (http: // stackoverflow।कॉम/ए/34 9 37706/6811411) – LotPings

+0

एक डीबगर में सीएमडी चलाएं। मेरा जवाब यहां देखें http://stackoverflow.com/questions/38148571/vb6-debugging-compiled। 'x cmd। * 'इसमें' parse' और 'errorlevel' के साथ बहुत सारे फ़ंक्शन दिखाता है। –

+1

यदि आपका प्रश्न है "यह क्यों होता है?", तो माइक्रोसॉफ्ट केवल जानता है ... इस व्यवहार को [इस उत्तर] पर दस्तावेज किया गया है (http://stackoverflow.com/questions/34987885/what-are-the-errorlevel-values -सेट-बाय-आंतरिक-cmd-exe-command/34987886 # 34987886), अन्य मामलों के अलावा जो त्रुटि मान भी इस तरह से लौटाते हैं; "बाहर निकलें कोड प्रबंधन (तालिका 5)" के लिए देखो। बीटीडब्लू, त्रुटि से 0 को रीसेट करने का एक आसान तरीका 'ver' कमांड के माध्यम से है ... – Aacini

उत्तर

5

पर वर्णित व्यवहार मनाया मैं पहली बार File redirection in Windows and %errorlevel% लगभग 5 साल पहले इस अतार्किक व्यवहार की खोज की। दो महीने बाद मैंने आरडी (आरएमडीआईआर) कमांड के साथ batch: Exit code for "rd" is 0 on error as well पर एक ही समस्या की खोज की। उस अंतिम प्रश्न का शीर्षक वास्तव में भ्रामक है, क्योंकि एक असफल आरडी का रिटर्न कोड शून्य नहीं है, लेकिन आदेश को निष्पादित करने से पहले ERRORLEVEL किसी भी मूल्य से अपरिवर्तित है। यदि रिटर्न कोड वास्तव में 0 था, तो || ऑपरेटर आग नहीं लगेगा।

Is all this only possible with (failed) redirections, because such are handled before any commands are executed?

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

So what is the connection between the ErrorLevel value and the || operator, why is ErrorLevel affected by ||? Is || copying the exit code to ErrorLevel?

दो अलग त्रुटि संबंधित मूल्यों कि ट्रैक करना आवश्यक हैं - 1) किसी भी आदेश (या आपरेशन) वापसी कोड (निकास कोड), और 2) ERRORLEVEL। रिटर्न कोड क्षणिक होते हैं - उन्हें प्रत्येक ऑपरेशन के बाद चेक किया जाना चाहिए। ERRORLEVEL समय के साथ "महत्वपूर्ण" त्रुटि राज्यों को बनाए रखने के लिए cmd.exe तरीका है। सभी त्रुटियों का पता लगाने का इरादा है, और ERRORLEVEL तदनुसार सेट किया जाना है। लेकिन ERRORLEVEL बैच डेवलपर्स के लिए बेकार होगा अगर इसे हर सफल ऑपरेशन के बाद हमेशा 0 तक साफ़ कर दिया गया हो। इसलिए cmd.exe के डिजाइनरों ने तार्किक विकल्प बनाने का प्रयास किया जब एक सफल कमांड ERRORLEVEL को साफ़ करता है, और जब यह पूर्व मान को संरक्षित करता है। मुझे यकीन नहीं है कि वे अपने विकल्पों में कितने बुद्धिमान थे, लेकिन मैंने Which cmd.exe internal commands clear the ERRORLEVEL to 0 upon success? पर नियमों को दस्तावेज करने का प्रयास किया है।

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

मुझे विश्वास है कि जहाँ भी एक त्रुटि cmd.exe के भीतर हो सकता है, डेवलपर्स, वापसी कोड का पता लगाने त्रुटि होने पर करने के लिए गैर शून्य ERRORLEVEL निर्धारित करते हैं, और उसके बाद किसी भी || कोड आग अगर यह नाटक में है करने वाले थे। लेकिन कुछ मामलों में डेवलपर ने नियमों से नहीं खेलकर एक बग पेश किया। विफल पुनर्निर्देशन या आरडी विफल होने के बाद, डेवलपर ने सफलतापूर्वक || कोड को बुलाया, लेकिन ERRORLEVEL को ठीक से सेट करने में विफल रहा।

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

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

1) The || operator sets at least 4 different ERRORLEVEL values when RD fails, depending on the type of error

2) || द्वारा निर्धारित ERRORLEVEL वही मान है जो सीएमडी/सी सेट, और सीएमडी/सी बस अंतिम कमांड/ऑपरेशन के रिटर्न कोड को आगे बढ़ाता है।

C:\test>(call)&rd . 
The process cannot access the file because it is being used by another process. 

C:\test>echo %errorlevel% 
0 

C:\test>(call)&rd . || rem 
The process cannot access the file because it is being used by another process. 

C:\test>echo %errorlevel% 
32 

C:\test>(call)&cmd /c rd . 
The process cannot access the file because it is being used by another process. 

C:\test>echo %errorlevel% 
32 

हालांकि, एक ऐसी विशिष्टता है जो इस सिद्धांत को अमान्य करने की धमकी देती है। यदि आप एक गैर-मौजूद कमांड को चलाने के लिए प्रयास करते हैं, तो आप एक 9009 त्रुटि मिलती है:

C:\test>invalidCommand 
'invalidCommand' is not recognized as an internal or external command, 
operable program or batch file. 

C:\test>echo %errorlevel% 
9009 

लेकिन अगर आप || ऑपरेटर, या सीएमडी/सी का उपयोग करें, तो ERRORLEVEL 1 है: -/

C:\test>invalidCommand || rem 
'invalidCommand' is not recognized as an internal or external command, 
operable program or batch file. 

C:\test>echo %errorlevel% 
1 

C:\test>(call) 

C:\test>cmd /c invalidCommand 
'invalidCommand' is not recognized as an internal or external command, 
operable program or batch file. 

C:\test>echo %errorlevel% 
1 

मैं यह मानकर इस विसंगति को अपने मन में हल करता हूं कि || की अनुपस्थिति में 9 00 ईआरआरओआरएलईएलईएल सेट करने के लिए जिम्मेदार कोड 9 00 9 उत्पन्न करने के लिए कुछ प्रकार के संदर्भ संवेदनशील अनुवाद करना चाहिए। लेकिन || हैंडलर अनुवाद से अवगत नहीं है, इसलिए यह केवल मूल वापसी कोड को ERRORLEVEL को आगे बढ़ाता है, जो 9 00 मान को ओवरराइट करता है वहाँ ईडी

मुझे किसी अन्य आदेश से अवगत नहीं है जो || का उपयोग किया गया था या नहीं, इस पर निर्भर करता है कि अलग-अलग गैर-शून्य ERRORLEVEL मान देते हैं।

Even more strangely, I could not observe the opposite behaviour -- ErrorLevel being reset to 0 by && --, when correctly reverting the test setup (that is, replacing (call) by (call) (to set ErrorLevel to 1 initially), clearing the read-only attribute of file test.tmp, creating file test.nil (first line not empty to avoid set /P to set ErrorLevel to 1), and using file extension .bat rather than .cmd for testing (to avoid set /P to reset ErrorLevel to 0)).

एक बार जब आप स्वीकार करते हैं कि सभी आदेशों सफलता पर ERRORLEVEL साफ़ करें, फिर इस व्यवहार सही समझ में आता है। यदि && संरक्षित त्रुटि को मिटा देना था तो पूर्व त्रुटियों को संरक्षित करने के लिए यह बहुत अच्छा नहीं होगा।

+0

शायद [यह] (http://stackoverflow.com/a/41324747/2861476) आपकी रूचि (अच्छी तरह से, या नहीं) हो सकता है –

3

टिप्पणी: इस उत्तर में मौजूद सभी सामग्री सिर्फ cmd.exe, स्रोत कोड है कि कोडांतरक उत्पादन उत्पन्न करता है की एक कटौती की कोडांतरक कोड/डिबग प्रतीकों में से एक निजी व्याख्या है। इस उत्तर में सभी कोड सिर्फ छद्म कोड है जो लगभग cmd.exe के अंदर क्या होता है, जो केवल प्रश्न के लिए प्रासंगिक भागों को दर्शाता है।

पहली बात यह है कि हम पता करने की जरूरत है कि errorlevel मूल्य आंतरिक चर _LastRetCode से लिया गया है है GetEnvVar समारोह से (कम से कम इस डिबग जानकारी प्रतीकों में नाम है)।

दूसरी बात यह जानना है कि आंतरिक रूप से cmd आदेशों में से अधिकांश कार्यों के एक सेट से जुड़े होते हैं। वे कार्य _LastRetCode में मान को बदलते हैं (या नहीं) लेकिन वे एक sucess/विफलता कोड (0/1 मान) भी लौटाते हैं जो आंतरिक रूप से यह निर्धारित करने के लिए उपयोग किया जाता है कि कोई त्रुटि है या नहीं।

echo आदेश के मामले में, eEcho समारोह उत्पादन कार्यक्षमता, जैसे

eEcho(x){ 
    .... 
    // Code to echo the required value 
    .... 
    return 0 
} 

है कि कोडित कुछ संभालती है, echo आदेश केवल एक ही निकास बिंदु है और (स्पष्ट _LastRetCode चर यह निर्धारित नहीं करता है/errorlevel मान नहीं बदलेगा) और हमेशा एक सशक्त कोड वापस कर देगा। echo विफल नहीं है (बैच बिंदु से, यह असफल हो सकता है और stderr पर लिख सकता है, लेकिन यह हमेशा 0 लौटाएगा और _LastRetCode कभी नहीं बदलेगा)।

लेकिन, यह फ़ंक्शन कैसे कहा जाता है ?, पुनर्निर्देशन कैसे बनाया जाता है?

Dispatch फ़ंक्शन है जो कॉल करने के लिए कमांड/फ़ंक्शन निर्धारित करता है और पहले आवश्यक पुनर्निर्देशन बनाने के लिए SetDir फ़ंक्शन (यदि आवश्यक हो) को कॉल करता है। एक बार पुनर्निर्देशन सेट हो जाने पर, GetFuncPtr फ़ंक्शन निष्पादित करने के लिए फ़ंक्शन का पता पुनर्प्राप्त करता है (cmd कमांड से जुड़े फ़ंक्शन) इसे कॉल करता है और कॉलर को इसका आउटपुट देता है।

Dispatch(x, x){ 
    .... 

    if (redirectionNeeded){ 
     ret = SetRedir(...) 
     if (ret != 0) return 1 
    } 

    .... 

    func = GetFuncPtr(...) 
    ret = func(...) 
    return ret 
} 

जबकि उन कार्यों से वापसी मान एक त्रुटि की उपस्थिति को प्रतिबिंबित (वे लौट सफलता पर विफलता पर 1, 0), और न ही Dispatch नहीं SetDir परिवर्तन _LastRetCode चर (जबकि यहाँ नहीं दिखाया, में से कोई भी वे चर के लिए कोई संदर्भ बनाते हैं), इसलिए कोई रीडायरेक्शन विफल होने पर errorlevel परिवर्तन नहीं होता है।

|| ऑपरेटर का उपयोग करते समय क्या परिवर्तन होता है?

|| ऑपरेटर eOr समारोह है कि कोडित है, हाँ अंदर नियंत्रित किया जाता है, फिर से कम या ज्यादा

eOr(x){ 
    ret = Dispatch(leftCommand) 
    if (ret == 0) return 0 

    _LastRetCode = ret 

    ret = Dispatch(rightCommand) 
    return ret 
} 

यह पहली बार बाईं ओर आदेश निष्पादित करता है। अगर यह कोई त्रुटि नहीं लौटाता है तो ऐसा करने के लिए कुछ भी नहीं है और एक सूस मूल्य के साथ बाहर निकलता है। लेकिन यदि बायां कमांड विफल रहता है, तो यह दाईं तरफ कमांड को कॉल करने से पहले _LastRetCode चर के अंदर लौटा हुआ मूल्य संग्रहीत करता है। प्रश्न में मामले में

(> "test.tmp" echo Text) || echo Fail 

निष्पादन

  • Dispatch (जो कार्यों बैच निष्पादन से निपटने से बुलाया गया है और एक पैरामीटर क्या निष्पादित करने के लिए के रूप में प्राप्त करता है) कॉल eOr
  • eOr है eEcho (ऑपरेटर के बाईं ओर) निष्पादित करने के लिए
  • Dispatch कॉल SetDir पुनर्निर्देशन
  • SetDir विफल रहता है और 1 रिटर्न बनाने के लिए
  • Dispatch रिटर्न 1SetDir रूप
  • eOr चेकों वापसी मान में नाकाम रही है (वहाँ _LastRetCode के लिए कोई परिवर्तन नहीं है) और यह 0 दिए गए मान नहीं है के रूप में _LastRetCode में संग्रहीत है। अब _LastRetCode1
  • eOr कॉल DispatcheEcho (ऑपरेटर के दाईं ओर) पर अमल करने
  • Dispatch कॉल GetFuncPtreEcho समारोह का पता पुनः प्राप्त करने के लिए है।
  • Dispatch कॉल लौटे समारोह सूचक और इसे वापस मान देता है (eEcho हमेशा 0 वापसी) पर से Dispatch (0)

है कि कैसे त्रुटि मान दिया (return 1)

  • eOr रिटर्न वापसी मान || ऑपरेटर का उपयोग करते समय _LastRetCode में एक विफल पुनर्निर्देशन ऑपरेशन संग्रहीत किया जाता है।

    eAnd फ़ंक्शन में क्या होता है, यानी && ऑपरेटर?

    eAnd(x){ 
        ret = Dispatch(leftCommand) 
        if (ret != 0) return ret 
    
        ret = Dispatch(rightCommand) 
        return ret 
    } 
    

    वहाँ _LastRetCode के लिए कोई परिवर्तन करता है, तो कहा जाता आदेशों चर परिवर्तन नहीं करता है वहाँ errorlevel के लिए कोई परिवर्तन नहीं है नहीं है, इसलिए,।

    कृपया, याद रखें कि यह केवल एक व्याख्या है जो मैं देखता हूं, असली कोड लगभग समान व्यवहार करेगा लेकिन यह लगभग निश्चित रूप से अलग होगा।

  • +1

    +1 - "निष्पादन है ... सूची" के साथ आपका संपादन महत्वपूर्ण बिट है जो आपके उत्तर को कुछ समझ में लाता है जो मुझे समझ में आता है :-) जैसा कि मैं सबसे अच्छा कह सकता हूं, हमारे उत्तर संगत हैं, सिवाय इसके कि आपके उत्तर का तात्पर्य है कि '&&' और '||' की एक श्रृंखला को रिक्त रूप से कार्यान्वित किया जाता है, जो दाएं से बाएं से स्टैक पर धक्का दिया जाता है, इसलिए अंतिम (बाएं सबसे अधिक) सशर्त ऑपरेटर के बाईं तरफ पहले निष्पादित किया जाता है। यह समझ में आता है और सुरुचिपूर्ण है। मेरा जवाब एक पुनरावर्तक बाएं से दाएं प्रसंस्करण को मानता है, जो काम भी कर सकता है, लेकिन कार्यान्वित करने के लिए और अधिक जटिल है, और स्पष्ट रूप से वास्तविकता नहीं :-) – dbenham

    +0

    @ डेबेनहम, मेरा इरादा जब मैंने आपको इस उत्तर की ओर इशारा किया था तो यह पुष्टि करने के लिए था कि आप इसे दबाएंगे। जहां तक ​​मैं देख सकता हूं (बस एक त्वरित दृश्य, मैं इसे फिर से बेहतर परीक्षण करूंगा) 'a && b || सी 'को' ईओआर (ईएंड (ए, बी), सी) के रूप में संभाला जाता है, और, हां, इसका तात्पर्य है कि 'ए' में एक सफलता और' बी' में विफल होने का अर्थ है 'सी' निष्पादित किया गया है –

    संबंधित मुद्दे