Estou tentando fazer um programa que selecione um nome de arquivo e depois o canalize para uma variável.
Minha tentativa:
Dir /B "C:\Program Files (x86)\ServerMaster\Internals\Versions\Van1" | (set /p myVar= & set myVar)
Depois de usado ele dá
myVar:1.14.4
o plano era entrar e ecoar o comando, então fiz assim
@echo off
Dir /B "C:\Program Files (x86)\ServerMaster\Internals\Versions\Van1" | (set /p myVar= & set myVar)
echo e %myVar%
cmd /k
mas tudo o que acontece é
e
O que estou fazendo de errado? Além disso, existe uma maneira de fazer isso com FOR?
Responder1
O que estou fazendo de errado?
Simples - você está tentando usar uma técnica que não funciona ;-) É impossível usar a saída de comando canalizada para definir variáveis de ambiente em seu ambiente pai devido à maneira como os pipes do Windows funcionam.
Seu script está sendo executado em um processo de comando - vamos chamá-lo de pai.
Um pipe do Windows inicia dois processos de comando filho, cada um com seu próprio espaço de ambiente separado. O lado esquerdo do tubo é executado no primeiro filho e o direito no segundo. O pipe envia a saída do primeiro filho como entrada para o segundo filho. O SET /P
define com sucesso a nova variável myVar no processo filho e o SET MYVAR
comando exibe o valor. Ótimo.
Mas então o canal termina e ambos os processos filhos são encerrados e seus ambientes associados desaparecem. Não há como transferir o valor do ambiente myVar do processo de pipe filho de volta para o ambiente pai.
Existem muitos efeitos colaterais surpreendentes no design do pipe do Windows que confundem qualquer pessoa que não entenda completamente o mecanismo. Verhttps://stackoverflow.com/q/8192318/1012053para mais informações. Recomendo a leitura da pergunta e de todas as respostas.
Existe uma maneira de fazer isso com FOR?
Sim, a maneira correta de capturar a saída de um comando em uma variável de ambiente é via FOR, especificamente FOR /F
como mostra DavidPostill. Embora haja um problema definido, um problema potencial e uma ineficiência em seu código.
Não há necessidade de usar USEBACKQ
ao processar a saída de um comando. É mais simples usar aspas simples regulares '
em vez de crase:for /f %%F in ('yourCommand') do ...
O erro é o uso de echo myVar:%myVar%
dentro do corpo do loop FOR. Isso exibirá o valor myVar
que existia antes da execução do loop porque a expansão percentual ocorre quando a linha é analisada e todo o loop FOR é analisado em uma única etapa. Você precisaria ativar a expansão atrasada ( setlocal enableDelayedExpansion
) e depois usar echo !myVar!
para ver o valor que foi definido dentro do loop.
Existe a possibilidade de outro problema - FOR /F analisará o valor em tokens delimitados por espaço e capturará apenas o primeiro token. Se o nome do seu arquivo contiver espaços, você não obterá o nome completo, a menos que use a opção DELIMS para não especificar nenhum delimitador.
for /f "delims=" %%F in (....) do ....
Mas não há necessidade de usar DIR /B
e FOR /F
. Você pode simplesmente usar um FOR
loop vanilla sem a /F
opção.
Por exemplo, o seguinte irá simplesmente listar todos os arquivos:
for %%F in ("C:\Program Files (x86)\ServerMaster\Internals\Versions\Van1\*") do echo %%~nxF
Especifica o ~nx
uso apenas do nome e da extensão do arquivo, descartando a unidade e o caminho da pasta.
Normalmente não há necessidade de usar uma variável de ambiente - basta usar a %%F
variável FOR dentro do seu loop para fazer seu trabalho.
Mas se você precisar de um valor fora do loop por algum motivo, certamente poderá fazer
for %%F in ("C:\Program Files (x86)\ServerMaster\Internals\Versions\Van1\*") do set "myVar=%%~nxF"
Mas isso capturará apenas o último arquivo iterado. A variável myVar será substituída a cada iteração. Seu design original teria capturado o primeiro arquivo listado (supondo que ele pudesse funcionar). Nenhuma das duas parece uma boa ideia, a menos que você saiba com certeza que a pasta Van1 contém apenas um único arquivo.
Se você precisar capturar todos os arquivos em variáveis, precisará usar um pseudo array. O lote não possui um conceito formal de array, mas pode ser facilmente emulado. Você pode encontrar muitos exemplos se simplesmente pesquisar na web por "matriz de arquivos em lote"