使用文件名掩码启动并发批处理并等待它们完成

Start concurrent batches with filename mask and wait for them to finish

我正在尝试启动固定数量的具有相似文件名的并发批处理进程,它们都在同一目录中:

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

感谢 and this 让我走到这一步。

非常感谢收到建议和想法!

编辑:看来我错过了这个问题的重点。尽管如此,我想我还是会把它留在这里供其他人查找。不过请随意投反对票。


这里有关于等待从属进程完成的其他建议。

您的主进程应该定期检查每个从属进程是否已完成。有很多方法可以做到这一点。我举几个例子:

在此之前,请在检查之间插入一个延迟,以免在像这样的紧密循环中消耗所有 CPU:

timeout /t 1 /nobreak

标记文件

让下级进程在即将完成时创建或删除文件

您可以这样创建文件:

echo ANYTHING>FILENAME

主脚本应定期检查这些文件是否存在,如下所示:

if exist FILENAME goto IT_EXISTS

当 all/none 个文件存在时,您的任务就完成了。

为了防止混乱,在 %temp% 目录中的 %random% 文件夹中创建文件,并通过参数 %1%2 ...[= 将其名称传递给下属27=]

通过window标题检查进程是否存在

运行 tasklist 并解析其输出以确定您的从属程序是否仍然 运行.

可能最简单的方法是使用 window 名称过滤掉 "your" 个进程。

像这样启动它们:

start "WINDOW_TITLE" "BATCH_FILE" ARGUMENTS

然后像这样搜索它们:

TASKLIST /fi "Windowtitle eq WINDOW_TITLE" | find ".exe"
if "%errorlevel%" == "0" goto PROCESS_EXISTS

如果 none 被发现,你的任务就完成了。

可在以下位置找到更多信息:A, B, C

通过PID检查进程是否存在

您可以使用进程的 PID 而不是 window 标题。

here

所述,运行 您使用 WMIC 的过程来获取它

外部程序

您可以下载或编写外部程序以方便inter-process通信。示例包括:

  • TCP 服务器和客户端 netcat
  • 使用 GnuWin32 coreutils 中的 mkfifo 创建命名管道并使用它
  • Windows 通过自定义信号量和事件 C/C#/AutoIT 程序
  • NirCmdPsExec 等实用程序可以简化 PID 检查程序

..还有更多

如果 none 的解决方案适合您,请编辑问题以缩小您的查询范围。

首先你必须了解这种在pause命令中使用管道的特殊方法是如何工作的,以及为什么它可以用于等待多个并行进程。

以您的第一个工作代码示例为起点

(
start "task1" cmd /C "TestMe1.bat"
start "task2" cmd /C "TestMe2.bat"
start "task3" cmd /C "TestMe3.bat"
) | pause

之所以有效,是因为 start 命令调用的每个新 CMD 实例都将在新控制台中启动,其 stdoutstderr 将重定向到该新控制台,因此子 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在命令行模式下执行,在这种模式下默认打开命令回显,所以每个命令在 FOR 主体内部将在执行前回显到标准输出(管道),这反过来会提供 pause 命令并在创建第一个子进程之前终止其进程,因此批处理文件执行无需等待子进程完成即可继续。

这可以通过将 @ 放在 do 之后轻松解决,这将关闭 FOR 主体内的命令回显。

(
for /f "delims=" %%x in ('dir /b /a-d TestMe*.bat') do @start "task" cmd /C "%%x"
) | pause>nul

您可能还想隐藏 pause 命令的输出,方法是将其重定向到 nul