What is the reason for batch file path referenced with %~dp0 sometimes changes on changing directory?
batch-file, c#
Solution
This question started the discussion on this point, and some testing was done to determine why. So, after some debugging inside `cmd.exe` ... (this is for a 32 bit Windows XP cmd.exe but as the behaviour is consistent on newer system versions, probably the same or similar code is used)
Inside Jeb's answer is stated
It's a problem with the quotes and %~0.
cmd.exe handles %~0 in a special way
and here Jeb is correct.
Inside the current context of the running batch file there is a reference to the current batch file, a "variable" containing the full path and file name of the running batch file.
When a variable is accessed, its value is retrieved from a list of available variables but if the variable requested is `%0`, and some modifier has been requested (`~` is used) then the data in the running batch reference "variable" is used.
But the usage of `~` has another effect in the variables. If the value is quoted, quotes are removed. And here there is a bug in the code. It is coded something like (here simplified assembler to pseudocode)
value = varList[varName]
if (value && value[0] == quote ){
value = unquote(value)
} else if (varName == '0') {
value = batchFullName
}
And yes, this means that when the batch file is quoted, the first part of the `if` is executed and the full reference to the batch file is not used, instead the value retrieved is the string used to reference the batch file when calling it.
What happens then? If when the batch file was called the full path was used, then there will be no problem. But if the full path is not used in the call, any element in the path not present in the batch call needs to be retrieved. This retrieval assumes relative paths.
A simple batch file (`test.cmd`)
@echo off
echo %~f0
When called using `test` (no extension, no quotes), we obtain `c:\somewhere\test.cmd`
When called using `"test"` (no extension, quotes), we obtain `c:\somewhere\test`
In the first case, without quotes, the correct internal value is used. In the second case, as the call is quoted, the string used to call the batch file (`"test"`) is unquoted and used. As we are requesting a full path, it is considered a relative reference to something called `test`.
This is the why. How to solve?
From the C# code
Don't use quotes : `cmd /c batchfile.cmd`
If quotes are needed, use the full path in the call to the batch file. That way `%0` contains all the needed information.
From the batch file
Batch file can be invoked in any way from any place. The only reliable way to retrieve the information of the current batch file is to use a subroutine. If any modifier (`~`) is used, the `%0` will use the internal "variable" to obtain the data.
@echo off
setlocal enableextensions disabledelayedexpansion
call :getCurrentBatch batch
echo %batch%
exit /b
:getCurrentBatch variableName
set "%~1=%~f0"
goto :eof
This will echo to console the full path to the current batch file independtly of how you call the file, with or without quotes.
note: Why does it work? Why the `%~f0` reference inside a subroutine return a different value? The data accessed from inside the subroutine is not the same. When the `call` is executed, a new batch file context is created in memory, and the internal "variable" is used to initialize this context.
Problem
I have a batch file with following content: ``` echo %~dp0 CD Arvind echo %~dp0 ``` Even after changing directory value of `%~dp0` is the same. However, if I run this batch file from CSharp program, the value of `%~dp0` changes after CD. It now points to new directory. Following is the code that I use: ``` Directory.SetCurrentDirectory(//Dir where batch file resides); ProcessStartInfo ProcessInfo; Process process = new Process(); ProcessInfo = new ProcessStartInfo("mybatfile.bat"); ProcessInfo.UseShellExecute = false; ProcessInfo.RedirectStandardOutput = true; process = Process.Start(ProcessInfo); process.WaitForExit(); ExitCode = process.ExitCode; process.Close(); ``` Why is there a difference in output on executing same script by different ways? Do I miss something here?