为什么控制台输出会导致批处理脚本无法干净退出?

3
我正在编写一个自引用的Windows 10 (home ed.)批处理脚本,用于在大量日志文件中定位字符串、创建结果文件,并在完成后使用notepad++打开日志文件。这个过程有时需要几分钟,因此我采用了自引用的方式来使控制回到原始命令窗口,直到日志文件被打开(并获得焦点)。
然而,当第二个带有“start”命令和“/b”开关的命令窗口包含至少一个“echo”命令时,它不能干净地退出,并要求我按Enter键才能完全退出该“嵌套”命令窗口。
我已将代码精简为9行,以便您能够明白我的意思。要查看其运行情况,请将以下内容保存为"test.bat"并从命令提示符中运行:
@echo off
if "%1" EQU "" call :noArgs & goto :done
echo There was at least one argument.
:done
exit /b
:noArgs
echo There were no arguments.
start "" /b cmd /c test.bat arg1
goto :eof

它会在提示下方打印出“没有参数”。然后提示符下会显示“至少有一个参数”,接着会停留在那里,等待按下 Enter 键才会返回控制到提示符。

如果你删除这行代码:

echo There was at least one argument.

第二个命令行窗口不再需要按Enter键退出。同样,如果将echo命令的输出重定向到文件中,则问题会消失。此问题也会在没有echo命令的情况下出现,但如果从type命令生成了输出,则似乎是由于存在某种形式的控制台输出。可以通过注释掉“echo”行以及第一行“@echo off”来轻松演示这一点 - 现在将命令回显到控制台上,它又在退出之前挂起。
我可以通过将“start”调用更改为以下内容来解决此问题:
start "" /min cmd /c test.bat arg1

然而,任何输出在最小化的窗口中不再容易看到,因此这是一个糟糕的解决方案。
我很想知道我发布的代码为什么会表现出它所做的事情,为什么它不会干净地退出而需要按下回车键。我唯一的线索来自于这个页面上矩阵中的“备注”列,该页面链接如下:关闭和退出批处理文件,其中指出,“确保控制台窗口中没有显示文本,以使其在批处理文件结束时自动关闭”。然而,这似乎只适用于Windows 9.x版本的command.com,而不适用于EG Windows 10或cmd.exe。
感谢任何意见/想法。
-s1m0n-

我的Windows 7 (x64)机器上出现了相同的问题。即使我只输入start /B(或等效的start /B cmd),然后在新的cmd实例中输入exit,我仍然需要额外的回车才能返回到主cmd实例...我不认为这与你提供的链接中的cmd有任何关系;我猜测这是由于start /B引起的...不幸的是,我无法为此提供解决方案... - aschipfl
@RyanBemrose,如果你在Windows 8.1控制台中键入start /B cmd /C echo Hi会发生什么?它会立即返回到主要的cmd实例而无需按下“enter”键吗?如果你输入start /B cmd /C break会发生什么? - aschipfl
我认为Harry Johnston是正确的,因为我可以输入命令,尽管没有可见的提示符(光标在最左边)。我无法弄清楚的是,可能意味着没有简单的解决方案,如何强制再次显示提示符,使其看起来正常而不是挂起/等待。-s1m0n- - s1m0n
这取决于你为什么要异步运行子进程。如果没有特别的原因,可以省略start命令,即只需输入cmd /c test.bat arg1。(虽然有可能在退出之前异步运行子进程并等待其完成,但这有点棘手。) - Harry Johnston
我异步运行子进程,因为它可能需要几分钟才能完成并显示其结果。同时,我希望恢复命令行控制权。我设法解决了控制权问题,但没有新的命令提示符,这是一种相当奇怪的方式。我研究了如何显示提示符环境变量,可以通过 for /F "tokens=1 delims=#" %%a in ('"prompt #%PROMPT%# & echo on & for %%b in (1) do rem"') do ( echo %%a ) 来实现。从那里,我引入了这行代码到父进程中 for /F %%a in ('call') do call,这样就解决了问题。我将在另一个评论中发布整个修复过程。 - s1m0n
1个回答

4

您误解了输出结果。如果我理解您的意思正确,它应该是这样的:

C:\working\test>test
There were no arguments.

C:\working\test>There was at least one argument.

发生的情况如下:
C:\working\test>                                    <---- output from first shell
                test                                <---- input
There were no arguments.                            <---- output from first shell
                                                    <---- output from first shell
C:\working\test>                                    <---- output from first shell
                There was at least one argument.    <---- output from second shell
                                                    <---- cursor is here

第二个 shell 是异步运行的 - 这就是 `start` 的作用 - 所以第一个 shell 已经完成了批处理作业并打印出下一个提示,当第二个 shell 开始打印输出时。
此时,第二个 shell 已经退出,第一个 shell 正在等待您输入命令。它不知道第二个 shell 已经打印了任何内容,因此它没有理由认为需要重新打印提示。但是如果您输入一个命令,它将正常工作。

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