为什么在调用嵌套批处理文件时,如果不在行前添加“call”,就会退出父批处理文件?

38
我知道如何使用call命令从父文件中调用嵌套的批处理文件,因为有很多相关资源可供参考: 然而,我不明白为什么从另一个批处理文件中调用另一个批处理文件会终止父进程。
举个具体例子,假设我有一个将独立批处理文件“链接”在一起的批处理文件,并且错误地没有在每一行前加上call
foo.bat
bar.bat

这样只会执行foo.bat然后退出。要正确执行这两个命令,我需要在每个语句前加上"call":

call foo.bat
call bar.bat
为什么第一个功能仍然存在?为什么没有更改?我注意到call是在20世纪80年代末推出的MS-DOS 3.3中引入的,所以这个功能是否仍然存在用于反向兼容性?
我想不出任何(实际)用途,但也许是因为我太习惯了“新”的编程技术。

3个回答

37

DOS使用简单的文本处理方式(当时需要在config.sys中设置FILES=20来允许 20 个文件句柄),所以打开文件,读取下一行,关闭文件,然后执行刚才读取的那一行。如果文件调用了另一个文件,则继续处理该文件,因此批处理文件只需要一个文件句柄。

直到微软加入了call命令,才有了返回原始文件的方法(不使用诸如将前一个文件名作为参数传递并使用临时文件让原始批处理文件知道它已经处理了一些内容,然后可以GOTO到文件的下一部分的技巧)。


好的,所以最初的功能只是被忽略了?call 的实现是因为人们通常像你描述的那样使用变通方法吗?他们没有仅为了向后兼容而覆盖最初的功能吗? - Brandon Amos
5
向后兼容性是让很多Win 3.0程序能够在Win 7上继续运行的原因。当时,我编写了一些汇编语言函数,使它成为一个程序,并能返回到批处理文件中。现在,我使用一个简单的程序来实现“choice”,它将错误级别设置为按下字符的ASCII值。 - SeanC
顺便提一下,读取一行代码并执行,再读取另一行代码等功能意味着您可以拥有自修改的批处理文件。我从未有过这样的故意尝试 - 但如果您有一个批处理文件从版本控制中检索(新版本),则可能会出现奇怪的结果,其中它将执行版本1直到执行检索操作的那一行,然后继续执行版本2。 - yoyo

8

正如Sean Cheshire所写,这是为了向后兼容性。

但是在不使用CALL的情况下从批处理文件中启动另一个批处理文件并不会终止父进程!
看起来是这样的,因为通常情况下第二个批处理程序退出后父进程不会再执行。
但在启动second.bat之前使用call命令将显示第一个批处理程序未被终止。

parent.bat

echo parent.bat
call :myLabel
echo back in parent.bat main
exit /b

:myLabel
second.bat & echo back in parent.bat
exit /b

second.bat

echo second.bat
exit /b

我在这里使用secpond.bat & echo back ...来避免cmd.exe的另一个bug/特性。
如果你只使用second.bat而没有任何额外的内容,它会同时启动second.bat并跳转到second.bat中的标签:myLabel


1
我想指出/B是在NT 4中引入的 - 在此之前,只有可怕的编程技巧可用。 - SeanC

0

Call 基本上是指“执行其他批处理文件,然后返回这里继续执行”。它自 DOS 3.3 左右就存在了,如果现在移除它,将会破坏所有向后兼容性(这就是为什么人们仍在使用批处理脚本的原因)。它也可以用于跳转到 :link 位置。

关于使用和语法的信息(供他人参考),您可以查看此MS TechNet链接

如果您需要新的功能,请使用 CMD 脚本或 PowerShell 脚本。


谢谢回答,但那不完全是我想问的。您也重复了我在问题中提供的一些信息。我知道 call 如何工作,对于不知道的人有很多参考资料。我想知道为什么另一种方式(不使用 call)会在批处理脚本中实际使用。如果批文件嵌套在另一个批文件中,为什么有人希望父级无论如何都停止? - Brandon Amos
2
因为维护一个堆栈来记录离开的位置会增加驻留内存段的大小,从而限制可以运行的程序的最大大小。一旦做出了这个决定,就不能改变,因为这会导致现有的批处理文件停止工作。 - Raymond Chen

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