Substituição de string em lote do Windows

Substituição de string em lote do Windows

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_ncomo 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 %%Gconstains !. 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.

informação relacionada