为什么在FORFILES脚本中不能使用CALL :label命令?

4
这段代码
forfiles /P %pathname% /M *.log /c "cmd /c echo @file"

欣然列出一堆文件。

我们可以使用内部命令的许多其他组合,例如将 ECHO 替换为 RENAMEMOVE,但不能用 CALL 呼叫标签名。

例如,将上面的 ECHO 替换为以下内容:

forfiles /P %pathname% /M *.log /c "cmd /c CALL :listit @file"
 : :
 : :
exit /b

:listit
echo %1
exit /b

我本以为它会产生同样的输出,但却出现了错误信息,通常如此。
Invalid attempt to call batch label outside of batch script.

这种行为并没有在微软的文档中提到,而且由于有完全足够的替代方案,我也没有去寻找使其生效的方法。

但是我对 "嗯,为什么不呢?!"很感兴趣,所以我想问一个问题:

为什么我们不能在 FORFILES 脚本中使用 CALL :label 命令?

谢谢。


2
cmd /c opens a new instance of cmd. So the following is just a single command line; there is no context of a batch file. You can use for instead: for %%a in ("%pathname%\*.log) do call :listit "%%a" - Stephan
这是因为你正在使用的调用指令是在另一个cmd.exe实例中运行,而不是作为批处理文件cmd.exe实例的一部分运行。 - Compo
@Compo 是的,我也想知道是否可能不是这种情况,所以我尝试了一个CALL :listit.bat @file作为替代方案,认为这样会起作用,但是嗯...它没有。 - ilinkcs
CALL :label 语句用于在当前批处理文件中调用标签。请改用 CALL listit.bat @file。参见 https://ss64.com/nt/call.html。 - DodgyCodeException
DodgyCodeException,感谢您的参考。是的,在提问之前我已经阅读了SS64和Rob van der Woude,但似乎没有给我答案。不过现在我已经清楚地理解了原则。感谢大家。 - ilinkcs
显示剩余3条评论
1个回答

6

正如@Stephan已经评论的那样,它不能使用标签,因为它在命令行环境中执行。

在大多数情况下,您可以使用更简单的FOR命令。

但是,在需要forfiles和标签的情况下,可以通过简单的技巧解决此问题。

  1. 在批处理文件开头,您需要添加一行FOR /F ... goto :%%L
  2. 当调用标签时,它必须被包含在forfiles ... call %~d0\:<myLabel>:\..\%~pn0中。
@echo off
FOR /F "tokens=3 delims=:" %%L in ("%~0") DO goto :%%L

...
forfiles /P %pathname% /M *.log /c "cmd /c CALL %~d0\:listit:\..\%~pn0 @file"
...

:listit
echo %1
exit /b

来自aschipfl的一条好建议:
为了增加对路径/文件名中空格的稳定性,可以将调用目标用引号括起来 call 0x22%~d0\:listit:\..\%~pn00x22 @file

这是仅适用于forfiles的特殊语法,其中0xHH是字符0x22="的十六进制代码。


谢谢Jeb,这是一段很棒的脚本。 - ilinkcs
2
因此,将跳转标签嵌入脚本路径的目的是不改变参数数量,从而不影响%*的输出,对吗?我会将脚本路径放在(转义)引号之间:0x22%~d0\:listit:\..\%~pn00x22... - aschipfl
1
@aschipfl 你是对的。这种技术不会影响脚本的正常行为,特别是当它已经使用了 %1 时... 而且它是一个通用的跳板到任何函数。我在我的批处理库中大量使用它。 - jeb
越来越好...这是对引擎室正在发生的事情的深刻赞赏。 - ilinkcs
@jeb你能为此提供一个参考吗?我尝试了,但它似乎没有跳转到指定的标签。就我所理解的,它基本上执行cmd /c D:\:mylabel:\..\myscript arg1,是吗?我也试着直接在命令行运行但它不起作用。 - kapitanluffy
@kapitanluffy,你在第一个 FOR /F "tokens=3 ... 中添加了一些调试代码吗?如果问题仍然存在,你应该提出一个单独的问题。 - jeb

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