我正在尝试启动固定数量的并发批处理,这些批处理具有相似的文件名,并且都在同一目录中:
TestMe1.bat
TestMe2.bat
TestMe3.bat
所有批次应同时开始,并且所有批次都应在该批次继续之前完成,例如一个master.bat:
echo Starting batches
(
start "task1" cmd /C "TestMe1.bat"
start "task2" cmd /C "TestMe2.bat"
start "task3" cmd /C "TestMe3.bat"
) | pause
echo All batches have stopped and we can safely continue
我正在尝试找到一种方法来启动目录中与TestMe * .bat匹配的所有批次,这样我就不必每次都制作一个新的master.bat文件。像这样的东西,但是,你知道,正在工作:
echo Starting batches
(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do start "task" cmd /C "%%x"
) | pause
echo All batches have stopped and we can safely continue
感谢收到建议和想法!
答案 0 :(得分:2)
首先,您必须了解这种通过pause
命令使用管道的特殊方法是如何工作的以及为什么可以将其用于等待多个并行进程。
以您的第一个工作代码示例为起点
(
start "task1" cmd /C "TestMe1.bat"
start "task2" cmd /C "TestMe2.bat"
start "task3" cmd /C "TestMe3.bat"
) | pause
之所以可行,是因为start
命令调用的每个CMD新实例都将在新控制台中启动,并将其stdout
和stderr
重定向到该新控制台,因此子CMD的输出将不会重定向到管道,因此pause
命令不会消耗它,该命令正在等待管道的输入。
但是,
仍然每个进程都继承了管道句柄,因此只要子进程仍处于活动状态,管道的句柄将保持打开状态。
因此,管道右侧的pause
命令将保持活动状态,等待管道左侧的输入,直到从管道(永远不会发生)或所有子级接收到输入为止进程已终止并关闭了管道的句柄。
因此,正在执行母版批处理文件的主CMD实例实际上正在等待pause
命令(管道的右侧)终止,而该命令又正在等待子进程(潜在的管道编写器)执行终止。
现在清楚了为什么您尝试的第二次涉及FOR
循环的代码无法正常工作。
(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do start "task" cmd /C "%%x"
) | pause
管道内的FOR
命令由子CMD在命令行模式下执行,在此模式下,默认情况下命令回显处于启用状态,因此{{1 }}主体将在执行之前被回显到标准输出(管道),依次馈送FOR
命令并在创建第一个子进程之前终止其进程,因此批处理文件的执行无需等待子进程完成。
可以通过将pause
放在@
之后将do
主体内部的回显命令关闭来轻松解决。
FOR
您可能还想通过将(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do @start "task" cmd /C "%%x"
) | pause>nul
命令的输出重定向到pause
答案 1 :(得分:1)
编辑:看来我错过了这个问题的重点。尽管如此,我还是想把这个留给别人找。不过请随意投票。
还有其他有关等待下级流程完成的建议。
您的主要流程应定期检查是否每个下属流程都已完成。有很多方法可以做到这一点。我列举几个:
在此之前,请在两次检查之间插入一个延迟,以免在这样的紧密循环中不消耗所有CPU:
timeout /t 1 /nobreak
使下级进程在即将完成时创建或删除文件
您可以创建如下文件:
echo ANYTHING>FILENAME
主脚本应定期检查这些文件是否存在,如下所示:
if exist FILENAME goto IT_EXISTS
当所有文件都不存在时,您的任务就完成了。
为防止混乱,请在%random%
目录下的%temp%
文件夹中创建文件,然后通过参数%1
,%2
...
运行tasklist
并解析其输出以确定您的从属程序是否仍在运行。
可能最简单的方法是使用窗口名称来过滤“您的”进程。
像这样启动它们:
start "WINDOW_TITLE" "BATCH_FILE" ARGUMENTS
然后像这样搜索它们:
TASKLIST /fi "Windowtitle eq WINDOW_TITLE" | find ".exe"
if "%errorlevel%" == "0" goto PROCESS_EXISTS
如果未找到您的任务,则结束。
您可以使用进程的PID来代替窗口标题。
要获取它,请按here所述,使用WMIC
运行您的进程
您可以下载或编写外部程序以促进进程间的通信。示例包括:
netcat
的TCP服务器和客户端mkfifo
创建一个命名管道并使用它NirCmd
和PsExec
之类的实用程序可以简化PID检查过程....更多
如果没有一种解决方案适合您,请编辑问题以缩小查询范围。