8

I'm on Windows 10 Enterprise x64. I have the following hierarchy of directories with a BAT file at the innermost level:

C:\
  dir\
    my files\
      run.bat

The BAT file contains the following lines:

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

(the meaning and use of %~dp0 is explained in the help topic for /? and in this answer)

If I run the BAT file from a command prompt whose current directory is C:\dir\my files, then I get a very reasonable result:

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

But if I invoke it from the parent directory C:\dir, I get:

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

Huh? Note that the innermost directory name is duplicated and there are some stray characters "\ at the end. Let's try it in a different way:

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

The stray characters are gone, but the directory name is still duplicated. What is an explanation for this? How can I modify the BAT file so that it gives the same output no matter from which directory it has been invoked?

Of course, my real scenario is more complicated than this simplified version. The value of %~dp0 is concatenated with other strings, assigned to environment variables, passed as an argument to other scripts, etc.

2 Answers2

5

This is a known bug/design deficiency within cmd.exe - %~dp0 and variants can give the wrong result if the path to the batch script was quoted.

There is a work-around. You can reliably get the value from within a CALLed subroutine (note that at least one modifier like ~d, ~f etc. must be used, else you get the subroutine :label)

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

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

-- SAMPLE OUTPUT --

d:\dir>"my files\test.bat"
From main fails: "d:\dir\my files\my files\"
From subroutine OK: "d:\dir\my files\"
dbenham
  • 11,194
  • 6
  • 30
  • 47
2

As a workaround, store the directory up-front:

set "dir=%~dp0"

This is because %0 is really path-as-invoked (like argv arg 0), so in your examples it's either "my files"\run.bat or "my files\run.bat".

When you do %~dp0, cmd.exe is building up the full path relative to the current directory and then extracting the parts you asked for.

After you pushd, the 'full path' (%~f0) is going to be either:

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

... and then trim off the filename to get your results.

porges
  • 121
  • 3
  • very nice explanation, but it is still a bug because it happens only when we use quotes as dbenham explained, no? – Badr Elmers May 13 '23 at 21:43