Nomes de diretório duplicados inesperados após a expansão de '% ~ dp0' em um arquivo BAT

7

Estou no Windows 10 Enterprise x64. Eu tenho a seguinte hierarquia de diretórios com um arquivo BAT no nível mais interno:

C:\
  dir\
    my files\
      run.bat

O arquivo BAT contém as seguintes linhas:

@pushd %~dp0
@echo %~dp0
@popd

(o significado eo uso de %~dp0 é explicado no tópico de ajuda for /? e em esta resposta )

Se eu executar o arquivo BAT a partir de um prompt de comando cujo diretório atual é C:\dir\my files , então eu obter um resultado muito razoável:

C:\dir\my files>run.bat
C:\dir\my files\

Mas, se eu invocá-lo a partir do diretório pai C:\dir , recebo:

C:\dir>"my files"\run.bat
C:\dir\my files\my files"\

Huh? Note que o nome do diretório mais interno é duplicado e há alguns caracteres perdidos "\ no final. Vamos tentar de uma maneira diferente:

C:\dir>"my files\run.bat"
C:\dir\my files\my files\

Os caracteres perdidos desapareceram, mas o nome do diretório ainda está duplicado. O que é uma explicação para isso? Como posso modificar o arquivo BAT para que ele forneça a mesma saída, independentemente do diretório em que foi invocado?

Claro, meu cenário real é mais complicado do que esta versão simplificada. O valor de %~dp0 é concatenado com outras cordas, atribuídos a variáveis de ambiente, passado como um argumento para outros scripts, etc.

    
por Vladimir Reshetnikov 13.07.2017 / 01:05

2 respostas

4

Esta é uma deficiência de design / bug conhecida em cmd.exe - %~dp0 e variantes podem fornecer o resultado errado se o caminho para o script em lote foi citado.

Existe uma solução alternativa. Você pode obter o valor de dentro de uma sub-rotina chamada CALL (note que pelo menos um modificador como ~d , ~f etc. deve ser usado, senão você pega a sub-rotina :label )

@echo off
setlocal
pushd %~dp0
echo From main fails: "%~dp0"
call :test
popd
exit /b

:test
echo From subroutine OK: "%~dp0"

- SAÍDA DE AMOSTRA -

d:\dir>"my files\test.bat"
From main fails: "d:\dir\my files\my files\"
From subroutine OK: "d:\dir\my files\"
    
por 13.07.2017 / 22:49
1

Como solução alternativa, armazene o diretório diretamente:

set "dir=%~dp0"

Isso ocorre porque %0 é realmente caminho-como-invocado (como argv arg 0), portanto, em seus exemplos, é "my files"\run.bat ou "my files\run.bat" .

Quando você executa %~dp0 , o cmd.exe está construindo o caminho completo relativo ao diretório atual e, em seguida, extrai as partes que você solicitou.

Depois de você pushd , o 'caminho completo' ( %~f0 ) será:

C:\dir\my files\my files\run.bat
C:\dir\my files\my files"\run.bat

... e depois retire o nome do arquivo para obter seus resultados.

    
por 13.07.2017 / 19:43