As variáveis de ambiente nos arquivos em lote são expandidas quando uma linha é analisada. No caso de blocos delimitados por parênteses (como o seu if defined
), o bloco inteiro conta como uma "linha" ou comando.
Isso significa que todas as ocorrências de% FOO% são substituídas por seus valores antes que o bloco seja executado. No seu caso sem nada, pois a variável ainda não tem um valor.
Para resolver isso, você pode ativar a expansão atrasada :
setlocal enabledelayedexpansion
A expansão atrasada faz com que variáveis delimitadas por pontos de exclamação ( !
) sejam avaliadas na execução, em vez de analisar, o que garantirá o comportamento correto no seu caso:
if not defined BAR (
set FOO=1
echo Foo: !FOO!
)
help set
detalha isso também:
Finalmente, foi adicionado suporte para expansão de variável de ambiente atrasada. Esse suporte está sempre desativado por padrão, mas pode ser ativado / desativado através da /V
opção de linha de comando para CMD.EXE
. VejoCMD /?
A expansão de variável de ambiente atrasada é útil para contornar as limitações da expansão atual, que ocorre quando uma linha de texto é lida, não quando é executada. O exemplo a seguir demonstra o problema com a 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 as instruções %VAR%
em ambas IF
são substituídas quando a primeira instrução IF é lida, uma vez que inclui logicamente o corpo da IF
, que é uma instrução composta. Portanto, o IF dentro da instrução composta está realmente comparando "antes" com "depois", o 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%
na medida em que não criará uma lista de arquivos no diretório atual, mas apenas definirá a LIST
variável para o último arquivo encontrado. Novamente, isso ocorre porque o %LIST%
é expandido apenas uma vez quando a FOR
instrução é lida e, nesse momento, a LIST
variável está vazia. Portanto, o loop FOR real que estamos executando é:
for %i in (*) do set LIST= %i
que continua definindo LIST
o último arquivo encontrado.
A expansão de variável de ambiente atrasada permite que você use um caractere diferente (o ponto de exclamação) para expandir as variáveis de ambiente no tempo de execução. Se a expansão variável atrasada estiver ativada, os exemplos acima podem ser escritos da seguinte maneira para funcionar como 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%
!
, use^^^!
(escape duas vezes). Caso contrário, o recurso "expansão atrasada" o consumirá.O mesmo comportamento também acontece quando os comandos estão em uma única linha (
&
é o separador de comandos):A explicação de Joey é a minha favorita. Observe, no entanto, que
enabledelayedexpansion
não funciona no Windows NT 4.0 (e não tenho certeza sobre o Windows 2000).Sobre sua pergunta de acompanhamento, não, não é possível
EnableDelayedExpansion
semsetlocal
. No entanto, o comportamento original que estava indo contra você pode ser usado para solucionar esse segundo problema: o truque éendlocal
na mesma linha em que você define novamente os valores das variáveis necessárias.Aqui está o seu
test.bat
modificado:Mas aqui está outra solução alternativa para esse problema: use um procedimento no mesmo arquivo em vez de um bloco embutido ou um arquivo externo.
fonte
Se não estiver funcionando dessa maneira, é provável que você tenha atrasado a expansão da variável de ambiente. Você pode desativá-lo
cmd /V:OFF
ou usar pontos de exclamação dentro do seu se:fonte
Isso acontece porque sua linha com
FOR
comando é avaliada apenas uma vez. Você precisa de uma maneira de reavaliar isso. Você pode simular uma expansão atrasada com oCALL
comando:fonte
Vale a pena notar que funciona se for um único comando na mesma linha.
por exemplo
será realmente definido
FOO
como 1. Você poderá fazer outra verificação, como:Ei, eu nunca disse que era bonito. Mas se você não quiser mexer com o setlocal ou com os negócios de expansão atrasada, é uma solução rápida.
fonte
Às vezes, como eu fazia no meu caso, quando você precisa ser compatível com sistemas legados, como servidores NT4 antigos, em que a expansão atrasada não está funcionando ou, por algum motivo, não está funcionando, você pode precisar de uma solução alternativa.
Caso você use a Expansão atrasada, também é recomendável incluir pelo menos o lote de check-in:
Caso você queira apenas uma abordagem mais legível e simples, aqui estão soluções alternativas:
que funciona assim:
e mais uma solução cmd pura:
funciona assim:
e esta é a solução de sintaxe completa SE ENTÃO ELSE:
funciona assim:
fonte