
Estou tentando entender como a substituição de strings em lotes do Windows realmente funciona e estou tendo 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 de 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_
Eu esperaria que o código fosse gerado _newp is: M_01_n
como a última linha. Estou realmente sem ideias aqui, alguém pode me indicar a direção certa?
BB
Responder1
Você tem alguns problemas:
%var%
a expansão ocorre quando a instrução é analisada e todo o bloco de código entre parênteses é analisado em uma única passagem, antes que qualquer comando seja executado. Portanto, o valor é o valor que existia antes do início do loop. A solução é a expansão atrasada, que ocorre à medida que 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 é uma fera complicada(e também mal documentado). Existem vários pontos onde vários tipos de variáveis são expandidos, e você deve entendê-los completamente se realmente quiser aproveitar ao máximo a programação em lote. Está tudo explicado no link, mas resumindo, a ordem de expansão é:
1) % de expansão - Parâmetro:echo %1
ouVariável de ambiente: echo %var%
---- A maior parte da análise já foi concluída ----
2) PARA expansão de variável: for %%A in (*) do echo %%A
3) Expansão de variável de ambiente atrasada: echo !var!
4) CALL% de expansão - Parâmetro:call echo %%1
ouVariável de ambiente: call echo %%var%%
5) Expansão da variável de ambiente SET /A: `set /a "value=var+1"
Observe que a expansão atrasada requer que a expansão atrasada seja habilitada viaSETLOCAL EnableDelayedExpansion
O código a seguir usando 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
constains !
. Isto 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 torna-se muito significativa se executada milhares de vezes em 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
)
Responder2
além da resposta de 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 funcionar você tem que setlocal EnableDelayedExpansion
existe uma explicação sobre isso na ajuda do cmd, embora não seja óbvio qual comando ajuda explica isso! Isso éset /?
set /?
Finalmente, foi adicionado suporte para expansão atrasada de variáveis de ambiente. Esse suporte está sempre desativado por padrão, mas pode ser ativado/desativado por meio da opção de linha de comando /V para CMD.EXE. Consulte CMD /?
A expansão atrasada da variável de ambiente é útil para contornar as limitações da expansão atual que acontece quando uma linha de texto é lida, não quando é executada. O exemplo a seguir demonstra o problema com expansão imediata de variáveis:
set VAR=before if "%VAR%" == "before" ( set VAR=after if "%VAR%" == "after" @echo If you see this, it worked )
nunca exibiria a mensagem, pois o %VAR% em AMBAS as instruções IF é substituído quando a primeira instrução IF é lida, pois inclui logicamente o corpo do IF, que é uma instrução composta. Portanto, o SE dentro da instrução composta está realmente comparando “antes” com “depois”, que nunca será igual. Da mesma forma, o exemplo a seguir não funcionará conforme o esperado:
set LIST= for %i in (*) do set LIST=%LIST% %i echo %LIST%
pois NÃO criará uma lista de arquivos no diretório atual, mas apenas definirá a variável LIST para o último arquivo encontrado. Novamente, isso ocorre porque %LIST% é expandido apenas uma vez quando a instrução FOR é lida e, nesse momento, a variável LIST está vazia. Portanto, o loop FOR real que estamos executando é:
for %i in (*) do set LIST= %i
que continua configurando LIST para o último arquivo encontrado.
A expansão atrasada de variáveis de ambiente permite usar um caractere diferente (o ponto de exclamação) para expandir variáveis de ambiente em tempo de execução. Se a expansão atrasada de variáveis estiver habilitada, os exemplos acima poderão ser escritos da seguinte forma para funcionar conforme pretendido:
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 e depois passando para cmd/v:on A maioria usa cmd .v:off até mesmo especialistas (talvez porque outras coisas possam ser interpretadas de maneira diferente com ele), então estou apenas usando isso para mostrar você que pode tentar o !var! notação dentro do 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:\>
aliás, se você tivesse cmd /v:on ou em um arquivo em lote, EnableDelayedExpansion ativado, como Dave mostra, você teria que estar ciente de ! sendo um caractere especial, então você teria um problema se o ! estava dentro de %var%. Então essa seria uma razão pela qual as pessoas não ativam o modo em tempo integral, pode haver outras razões.