IF के अंदर बैच फ़ाइल में चर सेट नहीं किए जा रहे हैं?


69

मेरे पास बहुत ही सरल बैच फ़ाइलों के दो उदाहरण हैं:

किसी वैरिएबल पर मान निर्दिष्ट करना:

@echo off
set FOO=1
echo FOO: %FOO%
pause
echo on

जो, उम्मीद के मुताबिक, परिणाम:

FOO: 1 
Press any key to continue . . .

हालाँकि, अगर मैं एक ही दो लाइनों को IF IF DEFINED ब्लॉक के अंदर रखता हूँ:

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: %FOO%
)
pause
echo on

यह अप्रत्याशित रूप से परिणाम:

FOO: 
Press any key to continue . . .

यह IF के साथ कुछ भी नहीं करना चाहिए , स्पष्ट रूप से ब्लॉक निष्पादित किया जा रहा है। यदि मैं BAR को ऊपर से परिभाषित करता हूं, तो, PAUSE कमांड से केवल पाठ प्रदर्शित होता है, जैसा कि अपेक्षित था।

क्या देता है?


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

अगर मुझे इस सरल उदाहरण बैच फ़ाइल को दूसरे के अंदर से कॉल करना था, तो उदाहरण FOO सेट करता है, लेकिन केवल LOCALLY।

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

testcaller.bat

@call test.bat 
@echo FOO: %FOO% 
@pause 

test.bat

@setlocal EnableDelayedExpansion 
@IF NOT DEFINED BAR ( 
    @set FOO=1 
    @echo FOO: !FOO! 
) 

यह प्रदर्शित करता है:

FOO: 1 
FOO: 
Press any key to continue . . . 

इस मामले में, ऐसा प्रतीत होता है कि मुझे CALLER में विलंबित विस्तार को सक्षम करना है, जो एक परेशानी हो सकती है।

जवाबों:


84

एक पंक्ति पार्स होने पर बैच फ़ाइलों में पर्यावरण चर का विस्तार किया जाता है। कोष्ठकों (आपके if defined) के द्वारा सीमांकित ब्लॉक के मामले में पूरे ब्लॉक को "लाइन" या कमांड के रूप में गिना जाता है।

इसका मतलब यह है कि ब्लॉक चलने से पहले % FOO% की सभी घटनाएं उनके मूल्यों द्वारा प्रतिस्थापित की जाती हैं। आपके मामले में कुछ भी नहीं है, क्योंकि चर का अभी तक कोई मूल्य नहीं है।

इसे हल करने के लिए आप विलंबित विस्तार को सक्षम कर सकते हैं :

setlocal enabledelayedexpansion

विलंबित विस्तार के कारण विस्मयादिबोधक चिह्न ( !) द्वारा वितरित किए गए चर को पार्स करने के बजाय निष्पादन पर मूल्यांकन किया जाना चाहिए जो आपके मामले में सही व्यवहार सुनिश्चित करेगा:

if not defined BAR (
    set FOO=1
    echo Foo: !FOO!
)

help set यह भी विवरण:

अंत में, विलंबित पर्यावरण चर विस्तार के लिए समर्थन जोड़ा गया है। यह समर्थन हमेशा डिफ़ॉल्ट रूप से अक्षम होता है, लेकिन /Vकमांड लाइन स्विच के माध्यम से सक्षम / अक्षम किया जा सकता है CMD.EXE। देखCMD /?

विलंबित पर्यावरण चर विस्तार वर्तमान विस्तार की सीमाओं के आसपास होने के लिए उपयोगी है जो तब होता है जब पाठ की एक पंक्ति पढ़ी जाती है, न कि जब इसे निष्पादित किया जाता है। निम्न उदाहरण तत्काल चर विस्तार के साथ समस्या को प्रदर्शित करता है:

set VAR=before
if "%VAR%" == "before" (
    set VAR=after
    if "%VAR%" == "after" @echo If you see this, it worked
)

संदेश प्रदर्शित कभी नहीं होगा, के बाद से %VAR%में दोनों IF बयान, जब पहली यदि बयान में कहा गया है प्रतिस्थापित किया गया है, क्योंकि यह तार्किक के शरीर में शामिल हैं IF, जो एक यौगिक बयान है। तो आईएफ के अंदर कंपाउंड स्टेटमेंट वास्तव में "पहले" के साथ "बाद" की तुलना कर रहा है जो कभी भी बराबर नहीं होगा। इसी तरह, निम्न उदाहरण अपेक्षा के अनुरूप काम नहीं करेगा:

set LIST=
for %i in (*) do set LIST=%LIST% %i
echo %LIST%

उस में यह वर्तमान निर्देशिका में फ़ाइलों की एक सूची नहीं बनाएगा , लेकिन इसके बजाय केवल LIST चर को अंतिम फ़ाइल में सेट करेगा । फिर, इसका कारण यह है कि %LIST%जब FOR स्टेटमेंट पढ़ा जाता है, तो केवल एक बार उसका विस्तार किया जाता है, और उस समय LISTवैरिएबल खाली होता है। इसलिए हम जिस वास्तविक लूप को निष्पादित कर रहे हैं वह है:

for %i in (*) do set LIST= %i

जो अभी LISTमिली अंतिम फ़ाइल में सेटिंग रखता है।

विलंबित पर्यावरण चर विस्तार आपको निष्पादन समय पर पर्यावरण चर का विस्तार करने के लिए एक अलग चरित्र (विस्मयादिबोधक चिह्न) का उपयोग करने की अनुमति देता है। यदि विलंबित चर विस्तार सक्षम किया जाता है, तो उपरोक्त उदाहरणों को निम्नानुसार लिखा जा सकता है:

set VAR=before
if "%VAR%" == "before" (
    set VAR=after
    if "!VAR!" == "after" @echo If you see this, it worked
)

set LIST=
for %i in (*) do set LIST=!LIST! %i
echo %LIST%

17
और अगर आपको एक प्रतिध्वनित करने की आवश्यकता है !, तो इसका उपयोग करें ^^^!(दो बार बच जाएं)। अन्यथा "विलंबित विस्तार" सुविधा इसे खा जाएगी।
ग्रैविटी डेस

4

एक ही व्यवहार तब भी होता है जब कमांड एक लाइन पर होते हैं ( &कमांड सेपरेटर है):

if not defined BAR set FOO=1& echo FOO: %FOO%

जॉय की व्याख्या मेरी पसंदीदा है। ध्यान दें कि enabledelayedexpansionWindows NT 4.0 पर काम नहीं करता है (और मुझे Windows 2000 के बारे में निश्चित नहीं है)।

आपके अनुवर्ती प्रश्न के बारे में, नहीं, यह EnableDelayedExpansionबिना संभव नहीं है setlocal। हालाँकि मूल व्यवहार जो आपके विरुद्ध चल रहा था, उसका उपयोग दूसरी समस्या के रूप में किया जा सकता है: चाल endlocalउसी रेखा पर होती है जहाँ आप फिर से उन चर के मानों को सेट करते हैं जिनकी आपको आवश्यकता होती है।

यहाँ आपका test.batसंशोधित है:

@echo off
setlocal EnableDelayedExpansion 
IF NOT DEFINED BAR ( 
    set FOO=1 
    echo FOO: !FOO! 
)
endlocal & set FOO=%FOO%

लेकिन यहां उस समस्या का एक और समाधान है: इनलाइन ब्लॉक या बाहरी फ़ाइल के बजाय उसी फ़ाइल में एक प्रक्रिया का उपयोग करें।

@echo off
if not defined BAR call :NotDefined
pause
goto :EOF

:NotDefined
set FOO=1
echo FOO: %FOO%
goto :EOF

"ट्रिक उसी लाइन पर एंडलोकल है" - इसके लिए धन्यवाद!
dforce

3

यदि यह इस तरह से काम नहीं कर रहा है, तो संभवतः आपको पर्यावरण चर विस्तार में देरी हो सकती है। आप या तो इसे बंद कर सकते हैं या cmd /V:OFFअपने अंदर विस्मयादिबोधक चिह्न का उपयोग कर सकते हैं :

@echo off
IF NOT DEFINED BAR (
    set FOO=1
    echo FOO: !FOO!
)
pause
echo on

3
विलंबित विस्तार को सक्षम करना केवल विस्मयादिबोधक चिह्न के शब्दार्थ को बदल देता है। सामान्य चर विस्तार पर इसका कोई प्रभाव नहीं पड़ता है। इसके अलावा, गलती से इसे सक्षम करने की संभावना बहुत कम है; जिन लोगों ने इसे सक्षम किया है वे आमतौर पर उद्देश्य पर ऐसा करते हैं।
जॉय

1

ऐसा इसलिए होता है क्योंकि FORकमांड के साथ आपकी लाइन केवल एक बार मूल्यांकन करती है। आपको इसके पुनर्मूल्यांकन के लिए किसी तरह की आवश्यकता है। आप CALLकमांड के साथ विलंबित विस्तार का अनुकरण कर सकते हैं :

for /l %%I in (0,1,5) do call echo %%RANDOM%%

विलंबित विस्तार ने काम नहीं किया, लेकिन असली हार्डलिंक को खोजने के लिए मेरी 3 लाइन cmd स्क्रिप्ट के लिए इस / कॉल / वर्क 7 पर काम किया है: @for / f "delims =" %% a ('bash ~ / perl / cwb2) .sh% * ') कॉल सेट CWD_REAL = %% एक प्रतिध्वनि cd / d% CWD_REAL% cd / d% CWD_REAL%
mosh

0

यह ध्यान देने योग्य है कि अगर यह एक ही लाइन पर एक कमांड है तो यह काम करता है।

जैसे

   if not defined BAR set FOO=1

वास्तव FOOमें 1 पर सेट होगा । आप फिर एक और जाँच कर सकते हैं जैसे:

   if not defined BAR echo FOO: %FOO%

अरे, मैंने कभी नहीं कहा कि यह सुंदर था। लेकिन अगर आप सेट्लोकल या विलंबित विस्तार व्यवसाय के साथ गड़बड़ नहीं करना चाहते हैं, तो यह एक त्वरित समाधान है।


0

कभी-कभी, जैसा कि मैंने अपने मामले में किया था, जब आपको पुराने NT4 सर्वर जैसे विरासत प्रणालियों के साथ संगत होने की आवश्यकता होती है, जहां विलंबित विस्तार काम नहीं कर रहा है या किसी कारण से काम नहीं कर रहा है, तो आपको वैकल्पिक समाधान की आवश्यकता हो सकती है।

यदि आप विलंबित विस्तार का उपयोग करेंगे, तो यह भी सिफारिश की जाएगी कि कम से कम बैच में जाँच शामिल की जानी चाहिए:

IF NOT %Comspec%==!Comspec! ECHO Delayed Expansion is NOT enabled. 

यदि आप अधिक पठनीय और सरल दृष्टिकोण चाहते हैं, तो यहां वैकल्पिक उपाय हैं:

REM Option 2: use `call` inside block statement
IF NOT DEFINED BAR2 ( 
  set FOO2=2 
  call echo FOO2: %%FOO2%%
)
echo FOO2: %FOO2%

जो इस तरह काम करता है:

>REM Option 2: use `call` inside block statement

>IF NOT DEFINED BAR2 (
 set FOO2=2
 call echo FOO2: %FOO2%
)
FOO2: 2

>echo FOO2: 2
FOO2: 2

और एक और शुद्ध cmd समाधान:

REM Option 3: use `goto` logic blocks
IF DEFINED BAR3 goto endbar3
  set FOO3=3 
  echo FOO3: %FOO3%
:endbar3
echo FOO3: %FOO3%

यह इस तरह काम करता है:

>REM Option 3: use `goto` logic blocks

>IF DEFINED BAR3 goto endbar3

>set FOO3=3

>echo FOO3: 3
FOO3: 3

>echo FOO3: 3
FOO3: 3

और यह पूर्ण है, तो ईएलएसई सिंटैक्स समाधान:

REM Full IF THEN ELSE solution
IF NOT DEFINED BAR4 goto elsebar4
REM this is TRUE condition, normal batch flow
  set FOO4=6
  echo FOO4: %FOO4%
  goto endbar4
:elsebar4
  set FOO4=4
  echo FOO4: %FOO4%
  set BAR4=4
:endbar4
echo FOO4: %FOO4%
echo BAR4: %BAR4%

यह इस तरह काम करता है:

>REM Full IF THEN ELSE solution

>IF NOT DEFINED BAR4 goto elsebar4

>set FOO4=4

>echo FOO4: 4
FOO4: 4

>set BAR4=4

>echo FOO4: 4
FOO4: 4

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