
Windows 배치에서 문자열 교체가 실제로 어떻게 작동하고 문제가 있는지 이해하려고 노력 중입니다.
@echo off
set var=wild
set varnew=%var:l=n%
echo var is: %var%
echo varnew is: %varnew%
작동합니다; 예상되는 출력을 생성합니다.
var is: wild
varnew is: wind
하지만 그렇지 않습니다(예: "Main" 디렉터리).
@echo off
for /D %%G IN (*) do (
setlocal
echo G is: %%G
set _srcp=%%G
echo _srcp is %_srcp%
rem set _newp=%_newp:ai=_01_% <-- confused variable
set _newp=%_srcp:ai=_01_%
echo._newp is: %_newp%
endlocal
)
다음과 같은 출력이 생성됩니다.
G is: Main
_srcp is Main
_newp is: %_srcp:ai=_01_
_newp is: M_01_n
코드가 마지막 줄로 생성될 것으로 예상합니다 . 여기에는 정말 아이디어가 없습니다. 누군가 나에게 올바른 방향을 알려줄 수 있습니까?
BB
답변1
몇 가지 문제가 있습니다.
%var%
확장은 명령문이 구문 분석될 때 발생하며 명령이 실행되기 전에 괄호로 묶인 전체 코드 블록이 한 번에 구문 분석됩니다. 따라서 값은 루프가 시작되기 전에 존재했던 값입니다. 해결책은 루프 내의 각 명령이 실행될 때 발생하는 지연된 확장입니다.논리가 잘못되었습니다. _newp 할당은 _srcp 값을 기반으로 해야 합니다.
CMD 프로세서는 복잡한 짐승이다(또한 문서화가 잘 안 되어 있음). 다양한 유형의 변수가 확장되는 지점이 여러 군데 있으며, 배치 프로그래밍을 최대한 활용하려면 이를 완전히 이해해야 합니다. 링크에 모두 설명되어 있지만 요약하면 확장 순서는 다음과 같습니다.
1) % 확장 - 매개변수:echo %1
또는환경 변수: echo %var%
---- 현재 대부분의 구문 분석이 완료되었습니다. ----
2) FOR 변수 확장: for %%A in (*) do echo %%A
3) 지연된 환경 변수 확장: echo !var!
4) CALL % 확장 - 매개변수:call echo %%1
또는환경변수: call echo %%var%%
5) SET /A 환경변수 확장: `set /a "value=var+1"
지연된 확장을 사용하려면 지연된 확장이 필요합니다.SETLOCAL EnableDelayedExpansion
지연된 확장을 사용하는 다음 코드는 원하는 결과를 제공합니다.
@echo off
for /D %%G in (*) do (
setlocal enableDelayedExpansion
echo G is: %%G
set "_srcp=%%G"
echo _srcp is !_srcp!
set "_newp=!_srcp:ai=_01_!"
echo _newp is: !_newp!
endlocal
)
%%G
지연된 확장은 FOR 변수 확장 후에 발생하므로 포함된 경우 결과가 손상됩니다 !
. 이는 추가 SETLOCAL을 사용하여 방지할 수 있습니다.
for /D %%G in (*) do (
setlocal disableDelayedExpansion
echo G is: %%G
set "_srcp=%%G"
setlocal enableDelayedExpansion
echo _srcp is !_srcp!
set "_newp=!_srcp:ai=_01_!"
echo _newp is: !_newp!
endlocal
endlocal
)
두 배의 백분율로 CALL을 사용하여 원하는 결과를 얻을 수도 있지만 이 방법은 훨씬 느립니다. 몇 번 실행하면 속도는 중요하지 않지만 루프에서 수천 번 실행하면 속도가 매우 중요해집니다.
@echo off
for /D %%G in (*) do (
setlocal
echo G is: %%G
set "_srcp=%%G"
call echo _srcp is %%_srcp%%
call set "_newp=%%_srcp:ai=_01_%%"
call echo _newp is: %%_newp%%
endlocal
)
답변2
davebenham의 답변에 더 나아가
FOR 또는 IF와 같은 블록 내에서 echo %var%를 수행하면 올바르게 작동하지 않습니다. %var% 변수는 업데이트되지 않습니다. !var를 사용해야 합니다! 그리고 !var!를 얻으려면 작동하려면 local EnableDelayedExpansion을 설정해야 합니다.
이에 대한 설명은 cmd 도움말에 있지만 어떤 명령의 도움말이 이를 설명하는지 명확하지 않습니다. 그것은set /?
set /?
마지막으로 지연된 환경 변수 확장에 대한 지원이 추가되었습니다. 이 지원은 기본적으로 항상 비활성화되어 있지만 CMD.EXE에 대한 /V 명령줄 스위치를 통해 활성화/비활성화할 수 있습니다. CMD /?를 참조하세요.
지연된 환경 변수 확장은 텍스트 줄을 실행할 때가 아니라 읽을 때 발생하는 현재 확장의 한계를 극복하는 데 유용합니다. 다음 예는 즉시변수 확장의 문제점을 보여줍니다.
set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
두 IF 문 모두의 %VAR%는 첫 번째 IF 문을 읽을 때 대체됩니다. 왜냐하면 복합 문인 IF의 본문이 논리적으로 포함되어 있기 때문입니다. 따라서 복합문 내부의 IF는 실제로 "이전"과 "이후"를 비교하고 있으며 결코 동일하지 않습니다. 마찬가지로 다음 예제는 예상대로 작동하지 않습니다.
set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
현재 디렉터리에 파일 목록을 작성하지 않고 대신 LIST 변수를 마지막으로 찾은 파일로 설정한다는 점에서 그렇습니다. 이는 FOR 문을 읽을 때 %LIST%가 한 번만 확장되는데 이때 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%
다음을 사용하여 명령줄에서 표기법을 테스트할 수도 있습니다.
cmd /e:on
기본값으로 시작하여 cmd /v:off로 이동한 다음 cmd/v:on으로 이동합니다. 전문가라도 대부분 cmd .v:off를 사용합니다(아마도 다른 항목이 다르게 해석될 수 있기 때문일 수 있음). 따라서 이를 보여주기 위해 사용하고 있습니다. !var!를 시도해 볼 수 있습니다. cmd 내에서 표기법을 사용하세요.
C:\>set a=5
C:\>echo %a%
5
C:\>echo !a!
!a!
C:\>cmd /v:on
Microsoft Wind
Copyright (c)
C:\>echo !a!
5
C:\>
btw, cmd /v:on 또는 배치 파일에서 EnableDelayedExpansion이 켜져 있는 경우 Dave가 보여주는 것처럼 주의해야 합니다! 특수 문자이기 때문에 ! %var% 내에 있었습니다. 그래서 사람들이 모드를 풀타임으로 설정하지 않는 이유는 다른 이유가 있을 수 있습니다.