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

时间:2018-11-23 17:38:04

标签: batch-file concurrency wait

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

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

感谢thisthis使我走到这一步。

感谢收到建议和想法!

2 个答案:

答案 0 :(得分:2)

首先,您必须了解这种通过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在命令行模式下执行,在此模式下,默认情况下命令回显处于启用状态,因此{{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

如果未找到您的任务,则结束。

更多信息可在以下位置找到:ABC

通过PID检查进程是否存在

您可以使用进程的PID来代替窗口标题。

要获取它,请按here所述,使用WMIC运行您的进程

外部程序

您可以下载或编写外部程序以促进进程间的通信。示例包括:

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

....更多

如果没有一种解决方案适合您,请编辑问题以缩小查询范围。