这个问题引起了关于这一点的讨论,并进行了一些测试以确定原因。因此,在cmd.exe
内进行了一些调试...(这是为一个32位Windows XP cmd.exe,但是由于行为在新系统版本上保持一致,可能使用相同或类似的代码)
Jeb的答案中指出:
It's a problem with the quotes and %~0.
cmd.exe handles %~0 in a special way
这里Jeb是正确的。
在当前批处理文件的上下文中,有一个对当前批处理文件的引用,即“变量”,包含运行中的批处理文件的完整路径和文件名。
当访问变量时,其值将从可用变量列表中检索,但如果请求的变量是%0
,并且已经请求了一些修改器(使用~
),则将使用运行批处理引用“变量”的数据。
但是,在变量中使用~
还会产生另一种影响。如果该值被引用,则引号将被删除。这里的代码存在一个bug。它被编写成以下形式(这里是简化后的汇编语言伪代码):
value = varList[varName]
if (value && value[0] == quote ){
value = unquote(value)
} else if (varName == '0') {
value = batchFullName
}
是的,这意味着当批处理文件被引用时,将执行if
的第一部分,并且不会使用批处理文件的完整引用,而是检索用于调用它的字符串。
那么会发生什么?如果在调用批处理文件时使用了完整路径,那么就不会有问题。但是,如果在调用中未使用完整路径,则需要检索路径中不存在于批处理调用中的任何元素。此检索假定为相对路径。
一个简单的批处理文件(test.cmd
)
@echo off
echo %~f0
当使用test
(无扩展名,无引号)调用时,我们得到c:\somewhere\test.cmd
当使用"test"
(无扩展名,有引号)调用时,我们得到c:\somewhere\test
在第一种情况下,没有引号,使用正确的内部值。在第二种情况下,由于调用被引用,用于调用批处理文件的字符串("test"
)将被取消引用并使用。因为我们正在请求完整路径,所以它被视为对称为test
的相对引用。
这就是原因。如何解决?
C#代码中
从批处理文件中
批处理文件可以从任何地方以任何方式调用。检索当前批处理文件的信息的唯一可靠方法是使用子例程。如果使用任何修改器(~
),则%0
将使用内部“变量”来获取数据。
@echo off
setlocal enableextensions disabledelayedexpansion
call :getCurrentBatch batch
echo %batch%
exit /b
:getCurrentBatch variableName
set "%~1=%~f0"
goto :eof
无论如何调用文件,带引号或不带引号,都会在控制台上输出当前批处理文件的完整路径。
注意: 为什么它可以工作?为什么子例程内的% ~f0
引用返回不同的值?从子例程内访问的数据不同。当执行call
时,在内存中创建了一个新的批处理文件上下文,并使用内部“变量”初始化此上下文。
cmd
中使用带引号的"mybatfile.cmd"
批处理文件来运行批处理时,可以复制这种行为。这正是通过Process.Start
运行时获得的调用方式,你可以通过echo %0
进行验证。 - Joeydp0”值可以正常工作,但是如果我通过Java ProcessBuilder启动相同的bat文件,“%dp0”会给出CWD。使用“cmd /c xyz.bat”似乎可以解决问题,但为什么呢? - Rich