在Windows批处理文件中,你能否链式执行不是另一个批处理文件的东西?

4

我理解如果你有两个批处理文件.bat.cmd,我们称它们为foobar,则应遵循以下规则:

不使用call

:: Welcome to foo.bat
@bar.bat
@echo We never get to this line because bar.bat is "chain-executed".

使用 call 命令:

:: Welcome to foo.bat
@call bar.bat
@echo This line is executed after bar.bat returns.

我的问题是:是否有一种方法执行相反的操作,即确保一个非批处理可执行文件被链接起来?
:: Welcome to foo.bat
@chain bar.exe
@echo Even though bar is now an .exe,  we never get to this line.
@echo At least, that would be the case if the "chain" command really existed.

换句话说,是否有一种方法在最后一个示例中执行虚构的chain命令的功能?


只需在行上放置bar.exe,它就会执行。当程序退出时,它将返回批处理文件。 - Squashman
@Squashman 这样做的缺点是,当从Windows资源管理器双击foo.bat时,它会保留其命令行窗口并等待bar.exe完成。 - jez
2
然后使用START命令。 - Squashman
嗯,实际上我的评论没有意义:当“bar”是批处理文件时,也会出现这种行为。使用“start”或不使用似乎与链接问题无关,我猜(??)在“start bar”或仅“bar”之后让执行回退到“foo”也没有任何不利影响。 - jez
1个回答

3

为了在单独的进程中运行可执行文件,需要使用命令start,并且还需要exit当前批处理或整个命令过程。

@echo off
echo Welcome to %~nx0
start "Title" bar.exe & exit /B
echo Even though bar is now an .exe, we never get to this line.

这个批处理文件会启动bar.exe,在新的控制台窗口中打开它,并将Title作为窗口标题,如果可执行文件是控制台应用程序的话。
然后,在start完成后,命令处理器无条件地执行exit /B,而bar.exe在单独的进程中运行,导致当前批处理文件的处理终止。
如果这个批处理文件没有使用命令call从另一个批处理文件中调用,那么命令处理器现在完成了批处理文件的处理,结果退出命令处理,除非使用cmd.exe选项/K调用批处理后,保持命令提示符窗口在批处理结束后仍然打开,但默认情况下不会这样做。
但是,如果这个批处理文件是从另一个批处理文件中使用call调用的,那么只有这个子批处理文件的处理完成后,命令处理器才会继续处理父批处理文件,而bar.exe则在一个单独的进程中运行。
@echo off
echo Welcome to %~nx0
start "Title" bar.exe & exit
echo Even though bar is now an .exe, we never get to this line.

在这个批处理代码中,命令exit没有选项/B,这将导致在单独的进程中启动bar.exe后,即使当前批处理文件是通过另一个批处理文件使用call调用的,即使批处理文件处理是通过带有参数/Kcmd.exe启动的,也会终止命令处理。

可以像下面显示的两个变体那样使用块,而不是无条件地使用运算符&连接两个命令startexit

只需退出当前批处理:

@echo off
echo Welcome to %~nx0
(
    start "Title" bar.exe
    exit /B
)
echo Even though bar is now an .exe, we never get to this line.

退出整个命令进程:

@echo off
echo Welcome to %~nx0
(
    start "Title" bar.exe
    exit
)
echo Even though bar is now an .exe, we never get to this line.

如果bar.exe是根据批处理文件中至少一个条件启动的,则使用退出当前批处理或整个命令处理的方式启动应用程序是有意义的。

注1:
也可以使用goto :EOF代替exit /B来结束当前批处理。

注2: goto :EOFexit /B都会导致子程序退出,如果该命令是批处理子程序的一部分,即通过call :label调用标签下面的代码,因为批处理子程序就像嵌入在主批处理文件中的子批处理文件。

以下是一些示例,以演示callexit /B的行为:

Test1.bat:

@echo off
echo Running %~nx0
call Test2.bat
echo Finished %~nx0

Test2.bat:

@echo off
echo Running %~nx0
Test3.bat
echo Finished %~nx0

Test3.bat:

@echo off
echo Finished %~nx0

在命令提示符窗口中运行Test1.bat会产生输出:
Running Test1.bat
Running Test2.bat
Finished Test3.bat
Finished Test1.bat

因此,由于命令处理器直接从Test3.bat返回到Test1.bat,所以缺少Finished Test2.bat这一行。

接下来,我们将编译以下C代码为控制台应用程序Test.exe

#include <stdio.h>

int main (int argc, char* argv[])
{
    if(argc > 1)
    {
        printf("Running %s with argument %s\n",argv[0],argv[1]);
    }
    else
    {
        printf("Running %s without an argument\n",argv[0]);
    }
    return 0;
}

我们使用Test.exe在以下两个批处理文件中:

Test4.bat:

@echo off
echo Running %~nx0
Test.exe 4
call Test5.bat
echo Finished %~nx0

Test5.bat:

@echo off
echo Running %~nx0
Test.exe 5
Test.exe 6 & exit /B
echo Finished %~nx0

在命令提示符窗口中运行Test4.bat会产生以下输出:

Running Test4.bat
Running Test.exe with argument 4
Running Test5.bat
Running Test.exe with argument 5
Running Test.exe with argument 6
Finished Test4.bat

因为命令处理器直接返回到Test4.bat并执行了参数为6的Test.exe,所以缺失了Finished Test5.bat行。
但是,如果使用bar&exit /B,那么bar是一个批处理文件(文件扩展名为bat或cmd),还是一个可执行文件(文件扩展名为exe或com)就变得非常重要。这可以通过将Test2.bat的代码更改为以下内容来演示:
@echo off
echo Running %~nx0
Test3.bat & exit /B
echo Finished %~nx0

在命令提示符窗口中运行Test1.bat,将输出以下信息:
Running Test1.bat
Running Test2.bat
Finished Test3.bat

在第二个批处理文件中添加 exit /B 后,命令处理器将在第一个批处理文件的上下文中解释第二个批处理文件中的 exit

很好,这让我了解到了关于exitexit /B的一些新知识。我猜我想要的行为对于非控制台进程来说最接近于start bar & exit /B,而对于控制台进程来说则是bar & exit /B。后者无论bar是真正的bar.bat还是bar.exe都具有相同的表现(尽管,在幕后,我认为两者之间的操作顺序略有不同 - 希望没有任何陷阱?) - jez
1
使用bar & exit /B时,无论bar是批处理文件还是可执行文件,都取决于当前批处理文件是子批处理还是主批处理。如果bar & exit /B在一个未经__call__从父批处理文件调用的批处理文件中,则处理方式相同。但是,如果这行代码在一个__called__的子批处理文件中,处理方式将不同,请参考上面的答案,并附带代码示例。 - Mofi
1
在批处理文件处理方面,“bar&exit / B”和“bar&goto:EOF”之间绝对没有任何区别。这可以通过在上述批处理文件中使用“goto:EOF”而不是“exit / B”来看到。输出(=处理)始终相同。 - Mofi
我无法复制您的断言,即“在第二个批处理文件中附加exit /B后,命令处理器将第二个批处理文件中的exit解释为第一个批处理文件上下文中的exit。” 我有两个文件:在one.cmd中,我有@echo hello 1\n@call two\n@echo goodbye 1,在two.cmd中,我有@echo hello 2\n@exit /B\n@echo goodbye 2。即使通过call调用了two,其中的exit也不会导致cmd退出one.cmd的上下文 - 即我仍然在控制台输出中看到“goodbye 1”。 - jez
是的,在您的示例中,one.cmd 调用 two.cmd 命令中的 exit /B 只会导致退出 two.cmd 的处理。但在我的示例中,Test1.bat 调用 包含行 Test3.bat&exit /B 无需调用Test2.bat,因此继续在 Test3.bat 上执行。Test2.bat 中的 exit /BTest3.bat 完成后执行,但已经在 Test1.bat 的上下文中,因此不会输出 Finished Test1.bat。但是,如果 Test3 是 EXE 而不是 BAT,则 Test2.bat 中的 exit /B 将只退出 Test2.bat 的处理。 - Mofi
显示剩余2条评论

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