Windows 배치에서 문자열 교체

Windows 배치에서 문자열 교체

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% 내에 있었습니다. 그래서 사람들이 모드를 풀타임으로 설정하지 않는 이유는 다른 이유가 있을 수 있습니다.

관련 정보