使用文件名掩码启动并发批处理并等待它们完成
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 被发现,你的任务就完成了。
通过PID检查进程是否存在
您可以使用进程的 PID 而不是 window 标题。
如 here
所述,运行 您使用 WMIC
的过程来获取它
外部程序
您可以下载或编写外部程序以方便inter-process通信。示例包括:
- TCP 服务器和客户端
netcat
- 使用 GnuWin32 coreutils 中的
mkfifo
创建命名管道并使用它
- Windows 通过自定义信号量和事件 C/C#/AutoIT 程序
NirCmd
和 PsExec
等实用程序可以简化 PID 检查程序
..还有更多
如果 none 的解决方案适合您,请编辑问题以缩小您的查询范围。
首先你必须了解这种在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在命令行模式下执行,在这种模式下默认打开命令回显,所以每个命令在 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
我正在尝试启动固定数量的具有相似文件名的并发批处理进程,它们都在同一目录中:
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
感谢
非常感谢收到建议和想法!
编辑:看来我错过了这个问题的重点。尽管如此,我想我还是会把它留在这里供其他人查找。不过请随意投反对票。
这里有关于等待从属进程完成的其他建议。
您的主进程应该定期检查每个从属进程是否已完成。有很多方法可以做到这一点。我举几个例子:
在此之前,请在检查之间插入一个延迟,以免在像这样的紧密循环中消耗所有 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 被发现,你的任务就完成了。
通过PID检查进程是否存在
您可以使用进程的 PID 而不是 window 标题。
如 here
所述,运行 您使用WMIC
的过程来获取它
外部程序
您可以下载或编写外部程序以方便inter-process通信。示例包括:
- TCP 服务器和客户端
netcat
- 使用 GnuWin32 coreutils 中的
mkfifo
创建命名管道并使用它 - Windows 通过自定义信号量和事件 C/C#/AutoIT 程序
NirCmd
和PsExec
等实用程序可以简化 PID 检查程序
..还有更多
如果 none 的解决方案适合您,请编辑问题以缩小您的查询范围。
首先你必须了解这种在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在命令行模式下执行,在这种模式下默认打开命令回显,所以每个命令在 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