运行批处理文件时,“Call”还是“不Call”?

89
如果在一个批处理文件中调用了另一个批处理文件,但仍有一些操作未完成,如何确保第一个批处理文件的调用在完成或出错后返回到调用它的文件?
示例:
CD:\MyFolder\MyFiles
Mybatfile.bat

Copy afile toHere
或者
CD:\MyFolder\MyFiles
CALL Mybatfile.bat

COPY afile toHere

使用CALLSTART,或者不使用它们之间有什么区别?这会对复制命令是否返回结果产生影响吗?


是的,你应该“调用”。 - Dave Newton
好的,公平。但是它们之间有什么区别?背后的机制是什么?START是什么?和call一样吗? - AltF4_
9
我不认为那是一个重复的问题。原帖主要想要一种机制来确保即使出现错误也能返回到父级。在发生致命语法错误时,CALL无法返回。那个相关联的问题根本没有解决这个问题。 - dbenham
4个回答

92

正如其他人所说,CALL 是在 .bat 文件中调用另一个批处理文件并返回给调用者的常规方式。

然而,如果被调用的批处理文件存在致命语法错误,或者调用的脚本在没有 /B 选项的情况下以 EXIT 结束,所有批处理文件的处理都将停止(控制将不会返回给调用者)。

如果您通过 CMD 命令执行第二个脚本,则可以保证控制权将返回给调用者(当然只要控制台窗口保持打开状态)。

cmd /c "calledFile.bat"

但这有一个限制,即由被调用批处理程序设置的环境变量在返回时不会被保留。

我不知道有什么好的解决方案可以保证在所有情况下都能返回并保留环境更改。

如果您确实需要在使用CMD时保留变量,则可以让“被调用”的脚本将变量更改写入临时文件,然后让调用者读取临时文件并重新建立变量。


2
我发现以下网页对于cmd /cCALL之间的区别非常有启发性:http://www.robvanderwoude.com/call.php - Rabarberski
无论你是通过callstart还是直接运行第二个批处理文件,第一个批处理文件设置的所有环境变量都将对第二个批处理文件可用。 - smwikipedia

51

call命令对于.bat或.cmd文件是必需的,否则控制权将不会返回给调用者。
对于exe文件,它并不需要。

startcall不同,它会创建一个新的cmd.exe实例,因此可以异步地运行一个“被调用”的批处理文件。


1
不禁想,什么时候会有人不使用CALL或START - 当所有脚本返回时终止调用者脚本似乎很奇怪? - Morten

13

`CALL'语句是在MS-DOS 3.3中引入的。

它用于在批处理文件中调用其他批处理文件,而不会中止调用批处理文件的执行,并为两个批处理文件使用相同的环境变量。

所以在您的情况下,解决方案是使用CALL


8

好的,实际上我并没有考虑过一个批处理文件(无论其类型是.bat还是.cmd)如果不使用call命令调用就不会返回。

但我自己一直在使用call命令,原因不同,我很惊讶竟然没有人提到过。也许我错过了。也许我是世界上唯一知道这个的人!:O

可能不是,但我要在这里分享这个非常有用的技巧。

如果你使用call命令,你可以使用二进制逻辑运算符根据ERRORLEVEL结果来决定如何继续执行。实际上,在DOS和COULDN'T中存在 && 和 || 运算符,不能以这种方式使用,这是最令人吃惊的。

测试这个技巧最简单的方法是使用记事本创建一个return.cmd文件,或者从命令提示符中这样做:

c:\> type con >return.cmd       

现在您会注意到光标跳到下一行并停留。请键入:
@exit /B %1

然后按下ENTER,再按下CTRL-Z,就会创建该文件。好了!现在你可以随意尝试以下两个例子:

call return.cmd 0 && echo 好极了!完全成功!(或掩盖...)

call return.cmd 123 || echo 哎呀!出了点问题。如果您想获得最少量的附加信息,可以检查ERRORLEVEL。

那又怎样呢?嗯,将0和123交换后再次运行它们,您应该会发现消息不会打印出来。

也许这个多行示例会更有意义。我经常使用它:

     call return.cmd 0 && @(
         echo Batch says it completed successfully^^!
     ) || @(
         echo Batch completed, but returned a 'falsey' value of sort.
         call echo The specific value returned was: %ERRORLEVEL%
     )     

(请注意,在第二个“echo”之前的||部分中出现了“call”。我相信这就是当时人们解决没有延迟扩展的方法。如果您启用了延迟扩展(通过批处理中的setlocal EnableDelayedExpansion OR使用cmd /v:on启动命令提示符),则只需执行!ERRORLEVEL!即可。)
......这就是我需要道歉并说如果你过去有if ERRORLEVEL的创伤,你应该停止阅读的地方。我明白的。相信我。我考虑过在fiverr上支付某人为我远程输入这个内容,但为了完整起见,我会为团队做出贡献,并提到您也可以执行以下操作来检查errorlevel:
    if ERRORLEVEL 123 @echo QUICK! MOTHERS, COVER YOUR CHILDREN'S EYES! FINGERS ARE BEING UNDONE! :'(

如果你之前从未打过这个命令,那太好了!你将长寿而不必阅读为什么没有得到期望结果的原因。正确的词汇是“残酷”,而不是“奇怪”。


然而,我想要传达的重要部分是,如果你尝试这样做但没有使用“call”,它将始终执行“true”分支。你可以自己试试!

如果我漏掉了什么,或者你知道更好的方法,请告诉我。我喜欢学习这样的东西!


我提到的额外信息:

我已经知道一段时间了,你可以在命令前放置重定向,就像这样:

    >nul echo. This won't be displayed!

但是我最近偶然发现,通过一些简单的操作,你也可以像个小白一样完成以下操作:
    echo A B>file.txt C

我非常惊讶地发现一个名为file.txt的文件,里面包含了"A B C"。看起来你可以把它们放在任何地方,甚至是命令内部。我从未见过有人这样做,也没有听说过,但我确实见过人们提到可以在行首加上它们。
也许这是Windows 10独有的bug之一。如果你有其他版本并想尝试一下,请告诉我你的发现,我会很感兴趣。
保持好奇!

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