Неожиданные повторяющиеся имена каталогов после раскрытия `% ~ dp0` в BAT-файле

792
Vladimir Reshetnikov

Я на Windows 10 Enterprise x64. У меня есть следующая иерархия каталогов с BAT-файлом на самом внутреннем уровне:

C:\ dir\ my files\ run.bat 

BAT-файл содержит следующие строки:

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

(значение и использование %~dp0объяснено в теме справки for /?и в этом ответе )

Если я запускаю BAT-файл из командной строки, текущим каталогом которой является C:\dir\my files, то я получаю очень разумный результат:

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

Но если я вызову его из родительского каталога C:\dir, я получу:

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

А? Обратите внимание, что внутреннее имя каталога дублируется, и "\в конце есть несколько случайных символов . Давайте попробуем это по-другому:

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

Заблудшие символы исчезли, но имя каталога все еще дублируется. Чем это объясняется? Как я могу изменить BAT-файл так, чтобы он давал одинаковые выходные данные независимо от того, из какого каталога он был вызван?

Конечно, мой реальный сценарий более сложен, чем эта упрощенная версия. Значение %~dp0сцепляется с другими строками, присваивается переменным окружения, передается в качестве аргумента другим сценариям и т. Д.

7
попробуйте переименовать файл в .cmd и посмотреть, если что-то изменится. Kirill Osenkov 6 лет назад 0
@KirillOsenkov Без изменений Vladimir Reshetnikov 6 лет назад 0
Я нашел разумный обходной путь: используйте переменную `% cd%` вместо того, чтобы нажать новый текущий каталог, но я до сих пор не понимаю, почему он не работал раньше. Vladimir Reshetnikov 6 лет назад 0
Попробуйте так: `. \" My files "\ run.bat` Nicke Manarin 6 лет назад 0
@NickeManarin Я не могу контролировать, как пользователь будет вызывать BAT-файл. У меня есть только контроль над его содержанием. Vladimir Reshetnikov 6 лет назад 2

2 ответа на вопрос

4
dbenham

Это известная ошибка / дефект дизайна в cmd.exe - %~dp0и варианты могут дать неправильный результат, если был указан путь к пакетному сценарию.

Есть обходной путь. Вы можете надежно получить значение из подпрограммы CALLed (обратите внимание, что должен использоваться хотя бы один модификатор, такой как ~dи ~fт. Д., Иначе вы получите подпрограмму :label)

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

- ВЫБОР ВЫБОРА -

d:\dir>"my files\test.bat" From main fails: "d:\dir\my files\my files\" From subroutine OK: "d:\dir\my files\" 
1
porges

В качестве обходного пути сохраните каталог заранее:

set "dir=%~dp0" 

Это потому, что %0действительно вызывается по пути (как argvаргумент 0), так что в ваших примерах это либо либо, "my files"\run.batлибо "my files\run.bat".

Когда вы это делаете %~dp0, cmd.exe создает полный путь относительно текущего каталога, а затем извлекает части, которые вы просили.

После вас pushd'полный путь' ( %~f0) будет либо:

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

... а затем обрежьте имя файла, чтобы получить результаты.