Substituição de string no lote do Windows

2

Estou tentando entender como a substituição de strings nos lotes do Windows realmente funciona e tem problemas.

@echo off
set var=wild
set varnew=%var:l=n%
echo var is: %var%
echo varnew is: %varnew%

funciona; ele gera a saída esperada:

var is: wild
varnew is: wind

Mas isso não acontece (exemplo diretório "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
                     )

Ele gera esta saída:

G is: Main
_srcp is Main
_newp is: %_srcp:ai=_01_

Espero que o código gere _newp is: M_01_n como a última linha. Estou realmente sem ideias aqui, alguém pode por favor me apontar na direção certa?

BB

    
por beerbear 05.08.2015 / 15:04

2 respostas

4

Você tem alguns problemas:

    A expansão
  • %var% ocorre quando a instrução é analisada e o bloco de código inteiro entre parênteses é analisado em uma passagem, antes que quaisquer comandos sejam executados. Portanto, o valor é o valor que existia antes do início do loop. A solução é a expansão atrasada, que ocorre quando cada comando dentro do loop está sendo executado.

  • Sua lógica está errada - a atribuição de _newp deve ser baseada no valor de _srcp

O processador CMD é um animal complicado (e também mal documentado). Existem vários pontos em que vários tipos de variáveis são expandidos e você deve compreendê-los completamente se realmente quiser aproveitar ao máximo a programação em lote. Tudo é explicado no link, mas em resumo, a ordem de expansão é:

1)% expansion - Parâmetro: echo %1 ou Variável de ambiente: echo %var%
---- A maioria da análise já foi concluída ----
2) PARA expansão de variável: for %%A in (*) do echo %%A
3) Expansão da variável de ambiente atrasada: echo !var!
4) Expansão% CALL - Parâmetro: call echo %%1 ou Variável de ambiente: call echo %%var%%
5) Expansão da variável de ambiente SET / A: valor 'set / a' = var + 1 "

Observe que a expansão atrasada exige que a expansão atrasada seja ativada por meio de SETLOCAL EnableDelayedExpansion

O código a seguir usando a expansão atrasada fornecerá o resultado que você procura:

@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
)

Observe que a expansão atrasada ocorre após a expansão da variável FOR, portanto, o resultado será corrompido se %%G for ! . Isso pode ser evitado por SETLOCAL adicional:

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
)

Você também pode obter o resultado desejado usando CALL com porcentagens duplas, mas isso é muito mais lento. A velocidade não é importante se executada algumas vezes, mas se torna muito significativa se executada milhares de vezes em um loop.

@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
)
    
por 05.08.2015 / 16:09
0

além da resposta da davebenham

fazer echo% var% dentro de um bloco como FOR ou IF, não funciona corretamente. As variáveis% var% simplesmente não são atualizadas. Você tem que usar! Var! e para obter! var! para trabalhar você tem que setlocal EnableDelayedExpansion

uma explicação disto existe em cmd help, embora não seja óbvio qual ajuda do comando explica isso! É set /?

set /?

Finally, support for delayed environment variable expansion has been added. This support is always disabled by default, but may be enabled/disabled via the /V command line switch to CMD.EXE. See CMD /?

Delayed environment variable expansion is useful for getting around the limitations of the current expansion which happens when a line of text is read, not when it is executed. The following example demonstrates the problem with immediate variable expansion:

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

would never display the message, since the %VAR% in BOTH IF statements is substituted when the first IF statement is read, since it logically includes the body of the IF, which is a compound statement. So the IF inside the compound statement is really comparing "before" with "after" which will never be equal. Similarly, the following example will not work as expected:

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

in that it will NOT build up a list of files in the current directory, but instead will just set the LIST variable to the last file found. Again, this is because the %LIST% is expanded just once when the FOR statement is read, and at that time the LIST variable is empty. So the actual FOR loop we are executing is:

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

which just keeps setting LIST to the last file found.

Delayed environment variable expansion allows you to use a different character (the exclamation mark) to expand environment variables at execution time. If delayed variable expansion is enabled, the above examples could be written as follows to work as intended:

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%

Você também pode testar a notação na linha de comando com

cmd /e:on

começando com o padrão, cmd / v: off, em seguida, movendo para cmd / v: on A maioria usa cmd .v: off mesmo para especialistas, (talvez porque outras coisas possam ser interpretadas diferentemente dele), então estou usando isso para mostrar que você pode experimentar o! var! notação dentro cmd com ele.

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, Se você tivesse cmd / v: em ou em um arquivo de lote, EnableDelayedExpansion ativado, como mostra Dave, você deve estar ciente disso! Sendo um personagem especial, então você teria um problema se o! estava dentro de um% var%. Então, essa seria uma razão pela qual as pessoas não apenas ativam o modo em tempo integral, pode haver outras razões.

    
por 11.08.2015 / 11:08