IF 내부에 배치 파일의 변수가 설정되지 않습니까?


69

매우 간단한 배치 파일의 두 가지 예가 있습니다.

변수에 값 할당하기 :

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

예상대로 다음과 같은 결과가 발생합니다.

FOO: 1 
Press any key to continue . . .

그러나 IF NOT DEFINED 블록 안에 동일한 두 줄을 배치하면 :

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

이로 인해 예기치 않은 결과가 발생합니다.

FOO: 
Press any key to continue . . .

이것은 IF와 관련 이 없어야 합니다. 블록이 분명히 실행되고 있습니다. if 위에 BAR을 정의하면 예상대로 PAUSE 명령의 텍스트 만 표시됩니다.

무엇을 제공합니까?


후속 질문 : setlocal없이 지연 확장을 활성화 할 수있는 방법이 있습니까?

이 간단한 예제 배치 파일을 다른 배치 파일에서 호출하는 경우 예제는 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
)

논리적으로 복합 문인의 본문을 포함하므로 첫 번째 IF 문을 읽을 때 명령문 모두%VAR% 에서 대체 되기 때문에 메시지를 표시하지 않습니다 . 따라서 복합 문장 내부의 IF는 실제로 "전"과 "후"를 비교하는 것입니다. 마찬가지로 다음 예제는 예상대로 작동하지 않습니다. IFIF

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

그에 의지 하지 만 설정합니다 대신 현재 디렉토리에있는 파일의 목록을 구축,하지만 LIST 발견 된 마지막 파일에 변수를. 다시 한 번, 이것은 명령문을 읽을 %LIST%때가 한 번만 확장 FOR되고 그 때 LIST변수가 비어 있기 때문 입니다. 따라서 실제 FOR 루프는 다음과 같습니다.

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
그리고을 반향해야하는 경우을 !사용하십시오 ^^^!(두 번 이스케이프). 그렇지 않으면 "지연된 확장"기능이이 기능을 사용합니다.
grawity

4

명령이 한 줄에있는 경우에도 동일한 동작이 발생 &합니다 (명령 구분 자임).

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

Joey의 설명은 내가 가장 좋아하는 것입니다. 그러나 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
지연 확장을 활성화 하면 느낌표의 의미 만 변경됩니다. 정규 변수 확장에는 아무런 영향을 미치지 않습니다. 또한 실수로 활성화하는 것은 거의 불가능합니다. 그것을 가능하게 한 사람들은 보통 의도적으로 그렇게합니다.
Joey

1

FOR명령이있는 줄은 한 번만 평가 되기 때문에 발생합니다 . 재평가 할 방법이 필요합니다. 다음 CALL명령을 사용하여 지연 확장을 시뮬레이션 할 수 있습니다 .

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

지연된 확장이 작동하지 않았지만이 / do call /은 win7에서 작동했습니다. 내 3 줄 cmd 스크립트가 실제 하드 링크를 찾기 위해 @for / f "delims ="%% a in ( 'bash ~ / perl / cwd2 .sh % * ') 호출 CWD_REAL = %% a echo cd / d % CWD_REAL % cd / d % CWD_REAL %
mosh

0

같은 줄에 단일 명령 인 경우 작동한다는 점은 주목할 가치가 있습니다.

예 :

   if not defined BAR set FOO=1

실제로 1로 설정 FOO됩니다. 다음과 같은 다른 검사를 수행 할 수 있습니다.

   if not defined BAR echo FOO: %FOO%

이봐, 난 예쁘다고 말한 적이 없어 그러나 setlocal 또는 지연된 확장 비즈니스를 망설이지 않으려면 빠른 해결 방법입니다.


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

그리고 이것은 IF THEN ELSE 구문 솔루션이면 충분합니다.

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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.