如何从 Windows shell 生成多个进程并等待它们全部完成?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/2190944/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-09 07:06:21  来源:igfitidea点击:

How to spawn several processes from the Windows shell and wait for them all to complete?

windowsscriptingbatch-file

提问by zr.

I want to do the following from a Windows batch script:

我想从 Windows 批处理脚本执行以下操作:

start proc1.exe

start proc2.exe

...

start procN.exe

<wait for all N processes to complete> <-- What do I put here?

How do I wait for all spawned processes to complete?

如何等待所有生成的进程完成?

采纳答案by Jeremy Stein

This is ugly, but you could wrap each command in anotherbatch file that runs the command and then signals that the command is complete. Your main batch file would call each of those batch files asynchronously and sit in a loop waiting for the signals.

这很难看,但您可以将每个命令包装在另一个运行该命令的批处理文件中,然后发出该命令已完成的信号。您的主批处理文件将异步调用每个批处理文件,并在循环中等待信号。

For example:

例如:

main.bat

主文件

start cmd /c proc1.bat
start cmd /c proc2.bat
:wait
sleep 1
IF NOT EXIST proc1done GOTO wait
IF NOT EXIST proc2done GOTO wait
del proc1done
del proc2done
echo All processes are complete

proc1.bat

proc1.bat

proc1.exe
echo Done > proc1done

The sleep command is available in Windows Server 2003 Resource Kit Tools. If you don't have that, you could use a ping on localhost just to slow down that tight loop.

sleep 命令在Windows Server 2003 Resource Kit Tools 中可用。如果你没有那个,你可以在本地主机上使用 ping 来减慢这个紧密的循环。

回答by leifel

I found a good solution in using an alternative shell called "Yori". You can read more about it and download it from http://www.malsmith.net/yori/. Yori is open source and its code repository is at https://github.com/malxau/yori.

我找到了一个很好的解决方案,即使用名为“Yori”的替代 shell。您可以阅读有关它的更多信息并从http://www.malsmith.net/yori/下载。Yori 是开源的,其代码库位于https://github.com/malxau/yori

Yori makes it easy to implement parallel process execution and wait for them to finish. In essence, what you need to do is to start the different sub-processes as so-called "jobs" and then use the internal command "wait" to wait for them to finish. You can start any process as a job by adding the special characters "&!" at the end of the command. Something like this:

Yori 可以轻松实现并行流程执行并等待它们完成。本质上,您需要做的是将不同的子流程作为所谓的“作业”启动,然后使用内部命令“wait”等待它们完成。您可以通过添加特殊字符“&!”将任何进程作为作业启动。在命令的末尾。像这样的东西:

ping localhost &!
ping someotherhost &!
wait

You can put your Yori-dependent commands in a separate batch-file (with the Yori-batch extension ".ys1") and invoke that from your Windows-batch for example with the following command:

您可以将依赖于 Yori 的命令放在一个单独的批处理文件中(带有 Yori 批处理扩展名“.ys1”),并从 Windows 批处理中调用它,例如使用以下命令:

echo Let Yori do the parallel execution of some lengthy operations...
start /wait /b /belownormal yori.exe -c "your-yori-script.ys1"
echo All the lengthy operations are finished, continue with the next steps...

回答by Oliver Zendel

Typically you would do something like this using Python, but here it goes (inspired by this post and others):

通常你会使用 Python 做这样的事情,但在这里(受这篇文章和其他人的启发):

calcFromList.bat < DesiredBatchFile > < TxtFile > [--async [SleepTimeBetweenSpawns]]

It will spawn one process for each line in TxtFile (the line is used to specify the individual parameters for the batch file).

它将为 TxtFile 中的每一行生成一个进程(该行用于指定批处理文件的各个参数)。

calcFromList.bat:

calcFromList.bat:

echo off

pushd \
%~d0
cd "%~dp0"

set CALL_BAT="%~1"
set FILE_CONTAINING_LIST="%~2"

if %CALL_BAT%=="" (goto badval)
if %FILE_CONTAINING_LIST%=="" (goto badval2)

if "%3"=="--async" goto async

for /f "delims=?" %%X in ('type %FILE_CONTAINING_LIST%') do (call %CALL_BAT% %%X)
goto end

:async
set /a AsyncCountNum=1

set SLEEP_VALUE_S=0

if not "%4"=="" (set SLEEP_VALUE_S=%4)
set AsyncParamBat=%CALL_BAT%

:: Find unique id even if called simultaneously (using simple collision detection)
:newid
timeout /t 1 /nobreak > NUL
set AsyncRand=%RANDOM:~-1%%RANDOM:~-2%%RANDOM:~-3%
set AsyncCountFilePrefix="%FILE_CONTAINING_LIST:"=%__%AsyncRand%"
set AsyncCollisionText="%~1;%~2;%~3;%~4;"
IF EXIST "%AsyncCountFilePrefix:"=%0" GOTO newid
echo %AsyncCollisionText% > "%AsyncCountFilePrefix:"=%_init_async_done.txt"
set /p AsyncCheckColision=<"%AsyncCountFilePrefix:"=%_init_async_done.txt"

if "%AsyncCollisionText:"=% "=="%AsyncCheckColision:"=%" GOTO idfound
timeout /t 1 /nobreak > NUL
GOTO newid
:idfound

echo Beginning spawning of processes (id = %AsyncRand%)...

for /f "delims=?" %%X in ('type %FILE_CONTAINING_LIST%') do (set AsyncParamCall="%%~X" & start "Batch (from id %AsyncRand%)" /D "%~dp0" "cmd /c asyncExec.bat" & set /a AsyncCountNum+=1 & timeout /t %SLEEP_VALUE_S% /nobreak > NUL)

set /a AsyncCountNum-=1
echo All %AsyncCountNum% processes spawned, waiting for their completion...
:wait
timeout /t 2 /nobreak > NUL
for /l %%x in (1, 1, %AsyncCountNum%) do (IF NOT EXIST "%AsyncCountFilePrefix:"=%_%%x_tmp_async_done.txt" GOTO wait )

echo Finished, cleaning up...
for /l %%x in (1, 1, %AsyncCountNum%) do (del /F /Q  "%AsyncCountFilePrefix:"=%_%%x_tmp_async_done.txt")
del /F /Q "%AsyncCountFilePrefix:"=%_init_async_done.txt"
goto end

start "Calib for %~TARGET_FOLDER%" /D "%~dp0" "cmd /k calibCamsSequence.bat"

goto end
:badval
echo No bat file specified! (first parameter)

goto end
:badval2
echo No list file specified! (second parameter)

:end
popd

asyncExec.bat:

asyncExec.bat:

@call %AsyncParamBat% %AsyncParamCall%
@echo "Done at %DATE% %TIME%" > "%AsyncCountFilePrefix:"=%_%AsyncCountNum%_tmp_async_done.txt"

回答by John Knoeller

You could do this if you write .NET code or Win32 C/C++ code to start the processes, but there is no way to do it in a batch file. Once you use startto make proc1.exe run asynchronously, there is no batch command that allows you to come back later and wait for it to complete.

如果您编写 .NET 代码或 Win32 C/C++ 代码来启动进程,则可以执行此操作,但无法在批处理文件中执行此操作。一旦您使用start使 proc1.exe 异步运行,就没有批处理命令允许您稍后返回并等待它完成。

But you can do this easily in one of the scripting languages designed for batch work, Python, WSH, etc.

但是,您可以使用一种专为批处理工作而设计的脚本语言 Python、WSH等轻松完成此操作。

For example, here's a simple script using Windows Script Host. WSH is included in all versions of Windows since Windows 98. So this script should run anywhere.

例如,这是一个使用 Windows Script Host 的简单脚本。自Windows 98以来,所有版本的 Windows 都包含 WSH 。所以这个脚本应该可以在任何地方运行。

This script starts two programs and waits for them both to finish. Save it as as start2.wsf. Then just use it in your batch file:

此脚本启动两个程序并等待它们完成。将其另存为start2.wsf. 然后只需在您的批处理文件中使用它:

start2.wsf "prog1.exe" "prog2.exe"


<package>
  <job id="Start 2 Procs">
    <runtime>
      <description>Start 2 programs and wait for them both to finish.
      </description>
      <unnamed
        name="program"
        helpstring="the program to run"
        many="false"
        required="2"
      />
      <example>
        Example: Start2.wsf "cl -c foo.obj" "cl -c bar.obj"
      </example>
    </runtime>

    <script language="JScript">

      if (WScript.Arguments.length < 2)
      {
        WScript.Arguments.ShowUsage();
        WScript.Quit();
      }

      var proc1 = WScript.Arguments.Unnamed.Item(0);
      var proc2 = WScript.Arguments.Unnamed.Item(1);

      var Shell = WScript.CreateObject("WScript.Shell");
      var oProc1 = Shell.Exec(proc1);
      var oProc2 = Shell.Exec(proc2);

      while (oProc1.Status == 0 && oProc2.Status == 0)
      {
        WScript.Sleep(1000);
      }
    </script>
  </job>
</package>