批处理脚本: %0 和 %~f0 有什么区别?

7

我有一个批处理脚本,我想获取脚本的完整路径。我很好奇,这两种方式有什么不同:

set scriptpath=%0

并且还有这个:

set scriptpath=%~f0

感谢您的帮助。

4
输入for /?并阅读,或者点击这里 - Aacini
2个回答

18
%0 是参数0的引用——即批处理文件的名称,其始终与命令行或另一个批处理文件中指定的完全相同。
因此,如果存储在 C:\Temp 中的批处理文件名为 Test.bat,例如在当前目录为 C:\ 的命令提示符窗口中使用 temp\test 执行批处理,则在批处理文件执行时,%0 将被替换为 temp\test
但是,%~f0 几乎总是被批处理文件的名称、文件扩展名和完整路径(完全限定文件名)替换,并且始终没有双引号,即使文件名或路径包含一个或多个字符,例如空格或 &()[]{}^=;!'+,`~,通常需要使用双引号。
因此,在具有以下行的存储在 C:\Temp 中的批处理文件 Test.bat 中:
@echo %0
@echo %~f0

在命令提示符窗口中使用"temp\test.bat"命令,当前目录为C:\,输出:

"temp\test.bat"
C:\Temp\Test.bat

这里可以看到所有的差异:

  • 完整的批处理文件名,
  • 始终不带双引号并且
  • 大小写正确。

有关 %~f0 和其他修饰符的说明可以在命令提示符窗口中运行 call /? 或在命令提示符窗口中运行 for /? 并阅读所有显示的页面中的CALL 命令输出的帮助或FOR 命令的帮助中找到。

注1:

如果需要在批处理文件后面使用当前目录已从批处理文件内部使用 CDPUSHD 修改,则应将完全限定的批处理文件名分配给环境变量。原因请参见为什么 %~dp0 引用的批处理文件路径在更改目录时会发生变化?上的答案。

注2:

仅使用 "temp\test"C:\ 运行上面的示例批处理文件将输出:

"temp\test"
C:\Temp\Test

文件扩展名丢失,这是由于第一个注释中描述的cmd错误引起的。使用不带双引号的temp\test启动批处理文件会产生预期的输出:

temp\test
C:\Temp\Test.bat

获取批处理文件的完全限定文件名并始终确定正确输出的最终解决方案是:


@echo off
goto Main
:GetFullBatchFileName
set "%1=%~f0" & goto :EOF
:Main
setlocal EnableExtensions DisableDelayedExpansion
echo %0
call :GetFullBatchFileName FullBatchFileName
setlocal EnableDelayedExpansion & echo !FullBatchFileName!& endlocal
endlocal

即使批处理文件 C:\Temp\Development & !Test!(!)\BestCode.batC:\ 执行,并以 "temp\development & !test!(!)\bestcode" 生成输出,此代码仍然有效:

"temp\development & !test!(!)\bestcode"
C:\Temp\Development & !Test!(!)\BestCode.bat

为什么要使用延迟扩展来输出完整的批处理文件名?

将文件/文件夹名称分配给环境变量,例如FullBatchFileName,并使用ECHO 命令输出时,如果没有使用引号括起来,则需要使用延迟扩展,否则文件/文件夹名称中的“&”符号将被解释为无条件的AND运算符,而非ECHO 命令要输出的文件/文件夹名称中的字面字符。

以下是未使用延迟扩展输出环境变量中文件/文件夹名称的例子:

@echo off
goto Main
:GetFullBatchFileName
set "%1=%~f0" & goto :EOF
:Main
setlocal EnableExtensions DisableDelayedExpansion
echo %0
call :GetFullBatchFileName FullBatchFileName
echo %FullBatchFileName%
endlocal

这个批处理文件代码存储在C:\Temp\Development & !Test!(!)\NotGood1.bat,执行后输出结果为"temp\development & !test!(!)\notgood1",并且是从C:\目录中执行的。

"temp\development & !test!(!)\notgood1"
C:\Temp\Development
'!Test!' is not recognized as an internal or external command,
operable program or batch file.

因此,Windows命令处理器cmd将环境变量FullBatchFileName中的&解释为无条件命令运算符AND。因此,ECHO仅输出完全限定批处理文件名的部分,直到&,其余部分由cmd.exe解释为在执行命令ECHO后要执行的第二个命令。在这种情况下,cmd.exe进行了大量的文件系统访问,以查找可由! Test!()\ NotGood1.bat 表示的可执行文件或脚本文件,并最终在未找到适当内容时输出错误消息。

为什么不从一开始就启用延迟扩展?

不能从一开始就启用延迟扩展,因为这会导致在文件/文件夹名称字符串中解释!作为延迟扩展变量引用的开头/结尾,如以下代码所示:

@echo off
goto Main
:GetFullBatchFileName
set "%1=%~f0" & goto :EOF
:Main
setlocal EnableExtensions EnableDelayedExpansion
set "Test="
echo %0
call :GetFullBatchFileName FullBatchFileName
echo !FullBatchFileName!
endlocal

这个批处理文件代码存储在 C:\Temp\Development & !Test!(!)\NotGood2.bat 中,执行时会从 C:\ 路径开始,并且输出中会包含 "temp\development & !test!(!)\notgood2"

"temp\development & ()\notgood2"
C:\Temp\Development & ()\NotGood2.bat

从输出结果可以看出,由于!test!!Test!被解释为延迟展开的变量引用,并且没有Test环境变量,两个输出字符串中都消失了第三个圆括号中的!。第三个!被移除是因为它被解释为延迟展开的变量引用的开始,而没有匹配的!标记该变量名的结尾。

另请参见:


7

%~f0 会将 %0 展开为一个完全限定的路径名。它们可能相等,也可能不相等,这取决于 %0 的原始值是什么。

这些修改器的含义在 For 命令的文档中隐藏。


1
与在命令提示符窗口中运行call /?命令的帮助输出相比,在运行for /?命令的帮助输出中,更少地隐藏了有关__call__命令的信息。 - Mofi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接