Toplu iş dosyasındaki değişkenler, IF içindeyken ayarlanmamıştır.


69

Çok basit bir toplu iş dosyası örneğim var:

Bir değişkene bir değer atama:

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

Beklendiği gibi, sonuçlanan:

FOO: 1 
Press any key to continue . . .

Ancak, aynı iki satırı IF NOT DEFINED bloğunun içine yerleştirirsem:

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

Bu beklenmedik şekilde sonuçlanır:

FOO: 
Press any key to continue . . .

Bunun IF ile ilgisi olmamalı , açıkça blok yürütülüyor. BAR'ı if'in üzerinde tanımlarsam, beklendiği gibi yalnızca PAUSE komutundaki metin görüntülenir.

Ne oluyor?


Takip eden soru: Setlocal olmadan gecikmeli genişlemeyi etkinleştirmenin bir yolu var mı?

Bu basit örnek toplu dosyayı başka bir yerden çağırmak isteseydim, örnek FOO'yu ayarlar, ama sadece LOCALLY.

Örneğin:

testcaller.bat

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

test.bat

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

Bu görüntüler:

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

Bu durumda, CALLER'de gecikmeli bir genişlemeyi etkinleştirmem gerekiyor, ki bu bir güçlük olabilir.

Yanıtlar:


84

Bir satır ayrıştırıldığında toplu iş dosyalarındaki ortam değişkenleri genişletilir. Parantezle (sizinkiyle) ayrılmış bloklarda if defined, tüm blok bir "satır" veya komut olarak sayılır.

Bu,% FOO% ' nun tüm oluşumlarının blok çalıştırılmadan önceki değerleriyle değiştirildiği anlamına gelir . Durumunuzda hiçbir şey yok, çünkü değişken henüz bir değere sahip değil.

Bunu çözmek için gecikmeli genişlemeyi etkinleştirebilirsiniz :

setlocal enabledelayedexpansion

Gecikmeli genişleme !, durumunuzda doğru davranışı sağlayacak olan ünlem işareti ( ) ile ayrıştırma yerine, ayrıştırma yerine yürütmede değerlendirilecek değişkenlere neden olur :

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

help set bunu da detaylandır:

Son olarak, gecikmiş ortam değişken genişlemesi için destek eklenmiştir. Bu destek varsayılan olarak her zaman devre dışıdır, ancak /Vkomut satırı düğmesi aracılığıyla etkinleştirilebilir / devre dışı bırakılabilir CMD.EXE. GörmekCMD /?

Gecikmeli ortam değişkeni genişletmesi, bir metin satırı okunduğunda, yürütüldüğünde değil, gerçekleşen geçerli genişletme sınırlamalarını aşmak için kullanışlıdır. Aşağıdaki örnek, anında değişken genişlemeyle ilgili sorunu göstermektedir:

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

çünkü mesajı görüntüler asla %VAR%içinde hem IF birinci IF deyimi okunduğunda mantıksal gövdesini içerir ifadeleri, ikame edilir IFbir birleşik deyimdir. Bu yüzden, bileşik ifadenin içindeki IF, asla "eşit" olmayacak olan "önce" ile "sonra" nı karşılaştırmaktadır. Benzer şekilde, aşağıdaki örnek beklendiği gibi çalışmayacak:

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

ki bu olacak değil sadece ayarlar yerine geçerli dizinde dosyaların bir listesini oluşturmak, ancak LIST bulunan en son dosyaya değişkeni. Yine, bunun nedeni, ifadenin okunduğunda %LIST%sadece bir kez genişletilmesi FORve o zaman LISTdeğişkenin boş olmasıdır. Yani yürüttüğümüz gerçek FOR döngüsü:

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

Bu sadece LISTbulunan son dosyayı ayarlamaya devam ediyor .

Gecikmeli ortam değişkeni genişletme, yürütme sırasında ortam değişkenlerini genişletmek için farklı bir karakter (ünlem işareti) kullanmanızı sağlar. Gecikmeli değişken genişlemesi etkinse, yukarıdaki örnekler istenildiği şekilde çalışmak için aşağıdaki gibi yazılabilir:

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
Ve bir yankı yapmanız gerekirse !, kullanın ^^^!(iki kez kaçış). Aksi takdirde "gecikmeli genleşme" özelliği onu yer.
Gravity

4

Aynı davranış, komutlar tek bir satırdayken de gerçekleşir ( &komut ayırıcıdır):

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

Joey'in açıklaması benim favorim. Ancak bunun enabledelayedexpansionWindows NT 4.0'da çalışmadığını unutmayın (ve Windows 2000 hakkında emin değilim).

Takip eden sorunuz hakkında, hayır, EnableDelayedExpansionolmadan mümkün değildir setlocal. Bununla birlikte, size karşı olan orijinal davranış, ikinci problemi gidermek için kullanılabilir: İşin püf noktası endlocal, ihtiyaç duyduğunuz değişkenlerin değerlerini yeniden ayarladığınız satırın aynısıdır.

İşte test.batdeğiştirilmiş:

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

Ancak bu soruna başka bir geçici çözüm: Aynı satırda satır içi blok veya harici dosya yerine bir prosedür kullanın.

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

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

"İşin püf noktası aynı çizgide endlocal" - Bunun için teşekkürler!
dforce

3

Bu şekilde çalışmıyorsa, ortam değişkeni genişlemesini geciktirmiş olabilirsiniz. Aşağıdaki durumlarda kapatabilir cmd /V:OFFveya ünlem işareti kullanabilirsiniz:

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

3
Gecikmeli genişlemeyi etkinleştirmek, yalnızca ünlem işaretinin anlamını değiştirir. Normal değişken genişleme üzerinde hiçbir etkisi yoktur. Ayrıca, yanlışlıkla sağlamak, çok düşük bir ihtimaldir; Bunu etkinleştiren insanlar genellikle bunu bilerek yaparlar.
Joey

1

Bunun nedeni komutlu satırınızın FORyalnızca bir kez değerlendirilmesidir. Yeniden değerlendirmek için bir yola ihtiyacın var. Gecikmeli bir genişletmeyi aşağıdaki CALLkomutla simüle edebilirsiniz :

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

Gecikmeli genişleme işe yaramadı, ancak bu / do çağrısı / win7'de çalıştı, 3 satır cmd betiğim için gerçek bağlantıyı buldum: @ for / f "delims =" %% a in ('bash ~ / perl / cwd2) .sh% * ') CWD_REAL = %% bir yankı cd / d% CWD_REAL% cd / d% CWD_REAL%
mosh

0

Aynı satırdaki tek bir komut ise işe yaradığını bildirmekte fayda var.

Örneğin

   if not defined BAR set FOO=1

aslında 1 olarak ayarlanacaktır FOO. Daha sonra aşağıdaki gibi başka bir kontrol yapabilirsiniz:

   if not defined BAR echo FOO: %FOO%

Hey, asla güzel olduğunu söylemedim. Ancak setlocal veya gecikmeli genişleme işiyle uğraşmak istemiyorsanız, bu hızlı bir çözümdür.


0

Bazen benim durumumda olduğu gibi, gecikmeli genişlemenin çalışmadığı ya da herhangi bir nedenle çalışmadığı eski NT4 sunucuları gibi eski sistemlerle uyumlu olmanız gerektiğinde alternatif bir çözüme ihtiyacınız olabilir.

Gecikmeli Genişletme'yi kullanmanız durumunda, en azından partide check-in yapılmasını da öneririz:

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

Daha okunaklı ve basit bir yaklaşım istiyorsanız, alternatif çözümler şunlardır:

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

Bu gibi çalışır:

>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

ve bir daha saf cmd çözümü:

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

bu gibi çalışır:

>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

ve bu tam ise DAHA ELSE sözdizimi çözümü:

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%

bu gibi çalışır:

>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
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.